一、原型模式
思考? 只要是数组就该能 push,因为这是数组类的功能,所以。而老师是 Teacher 类的实例,是要是老师就能 teach ,teach 是老师类的功能,不需要再每个实例上强调一遍。这是一个共有的特性,不该放在实例,而是应该放在一个公共的地方,以后只要是这个类的实例 就有这个功能。那它应该放在哪里呢? 原型对象 原型 prototype: 每一个函数(普通函数、构造函数【类】)都天生自带一个属性 prototype(原型)。这个属性的值是一个对象,用来存储当前类型的共有的属性和方法。保存在原型上面的属性和方法称为公有属性或公有方法。
1. 所以改造 Teacher 类型
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道授业解惑';
this.age = age;
this.subject = subject;
this.from = from;
}
Teacher.prototype.teach = function () {
console.log(`${this.name} 老师教 ${this.subject} 学科`);
};
console.log(Teacher.prototype);
let t1 = new Teacher('mabin', 18, 'js', 'zf');
let t2 = new Teacher('jiangwen', 19, '架构', 'zf');
t1.teach();
t2.teach();
console.log(t1.teach === t2.teach); // true
2. 如何检测属性公有还是私有
// hasOwnProperty() 方法:检测某个属性是否是对象的私有属性,如果是私有属性,返回 true,否则返回false;
let ary = [10,20,30];
console.log('0' in ary); // TRUE
console.log('push' in ary); // TRUE
console.log(ary.hasOwnProperty('0')); // TRUE
console.log(ary.hasOwnProperty('push')); // FALSE "push" 是它公有的属性不是私有的
console.log(Array.prototype.hasOwnProperty('push')); // TRUE 是公有还是私有属性 k,需要看相对
// 谁来说的
console.log(Array.prototype.hasOwnProperty('hasOwnProperty')); // FALSE
console.log(Object.prototype.hasOwnProperty('hasOwnProperty')); // TRUE
//自己堆中有的就是私有属性,需要基于 __proto__ 查找的就是公有属性(__proto__ 在 IE 浏览器中
// (EDGE 除外)给保护起来了,不让我们在代码中操作它)
// “in” :检测这个属性是否属于某个对象(不管是私有属性还是公有属性,只要是它的属性,结果就为 TRUE)
检测某个属性是否为对象的公有属性:hasPubProperty
//基于内置类原型扩展方法
Object.prototype.hasPubProperty = function (property) {
// 验证传递的属性名合法性(一般只能是数字或字符串等基本值)
let x = ["string", "number", "boolean"],
y = typeof property;
if (!x.includes(y)) return false;
// 开始校验是否为公有的属性(方法中的 THIS 就是要校验的对象)
let n = property in this,
m = this.hasOwnProperty(property);
return n && !m;
}
console.log(Array.prototype.hasPubProperty('push')); // FALSE
console.log([].hasPubProperty('push')); // TRUE
二、原型链
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道受业解惑';
this.age = age;
this.subject = subject;
this.from = from;
}
Teacher.prototype.teach = function () {
console.log(`${this.name} 老师教 ${this.subject} 学科`);
};
console.log(Teacher.prototype);
let t1 = new Teacher('mabin', 18, 'js', 'zf');
let t2 = new Teacher('jiangwen', 19, '架构', 'zf');
// 思考? 我把这个方法写在了 Teacher 的原型上,那么 t1 和 t2 是怎么找到的呢?
console.log(t1);
console.log(t2);
console.log(Teacher.prototype);
// 在私有属性中没有发现 teach 方法。我们发现这个 t1 和 t2 中有一个 __proto__ 属性,这个属性值里面有 teach 方法。那是怎么找过去呢? Teacher.prototype 中也有一个 __proto__,这是干嘛呢的?
// 原型链:对象的属性查找机制
// 每个实例都有一个属性 __proto__ 属性,它指向当前实例所属类的 prototype 对象。当我们访对象的一个的属性时,如果有,就使用私有属性,如果没有就通过实例 __proto__ 找到实例所属类的 prototype (原型)上查找,如果找到就使用 prototype 上的属性,如果还没找到,就通过 prototype 的 __proto__ 继续向上查找,一直找到 Object 的 prototype 就停止查找。如果还没找到就返回 undefined。
function Human(name, age, gf) {
this.name = name;
this.age = age;
this.gf = gf;
}
Human.prototype.eat = function () {
console.log('吃')
};
Human.prototype.sleep = function () {
console.log('睡')
};
Human.learn = function () {
console.log('学习')
};
let h = Human('陈*希', 40, '张*芝');
console.log(h.address);
三、原型链查找机制
- 先找自己私有的属性,有则调取使用,没有继续找
- 基于 proto 找所属类原型上的方法(Fn.prototype),如果还没有则继续基于 proto 往上找…一直找到Object.prototype 为止
- 如果找到了就使用,没找到返回 undefined
四、基于内置类的原型扩展方法
/*
* 基于内置类的原型扩展方法
* 在内置类原型上的方法,类所对应的实例可以直接调取使用,例如:实例.方法() ary.push()
* 如果我们也把自己写的方法放到原型上,那么当前类的实例也可以直接这样调取使用了,很方便
*
* 但是也有需要注意的地方
* 1.自己扩展的方法不能影响原有内置的方法(我们自己设置的方法最好加前缀:my)
* 2.扩展方法中的 THIS 一般都是当前类的实例(也就是要操作的值):实例.方法()
*/
~ function () {
/*
* myUnique : 实现数组去重
* @params
* @return
* [Array] 去重后的数组
*/
function myUnique() {
// 此时没有传递要操作的 ARY 进来,但是方法中的 THIS 是当前要操作的数组:ARY.MYUNIQUE()
let obj = {};
for (let i = 0; i < this.length; i++) {
let item = this[i];
if (typeof obj[item] !== 'undefined') {
this[i] = this[this.length - 1];
this.length--;
i--;
continue;
}
obj[item] = item;
}
obj = null;
// 保证当前方法执行完返回的结果依然是 ARRAY 类的一个实例
return this;
}
// 扩展到内置类的原型上
Array.prototype.myUnique = myUnique;
}();
let ary = [12, 23, 13, 12, 23, 24, 34, 13, 23];
// ary.myUnique(); 返回去重后的数组(也是 ARRAY 类的实例)
// ary.sort((a, b) => a - b); 返回排序后的数组
// 链式写法(保证返回值依然是当前类的实例 一般都会 RETURN THIS)
// ary.myUnique().sort((a, b) => a - b).reverse().slice(2).push('珠峰').concat(12);
// Uncaught TypeError: ary.myUnique(...).sort(...).reverse(...).slice(...).push(...).concat is not a function 执行完 push 返回的是一个数字(新增后数组的长度),不是数组了,不能在继续使用数
// 组的方法
ary.myUnique().sort((a, b) => a - b).reverse();
console.log(ary);