一、原型模式

思考? 只要是数组就该能 push,因为这是数组类的功能,所以。而老师是 Teacher 类的实例,是要是老师就能 teach ,teach 是老师类的功能,不需要再每个实例上强调一遍。这是一个共有的特性,不该放在实例,而是应该放在一个公共的地方,以后只要是这个类的实例 就有这个功能。那它应该放在哪里呢? 原型对象 原型 prototype: 每一个函数(普通函数、构造函数【类】)都天生自带一个属性 prototype(原型)。这个属性的值是一个对象,用来存储当前类型的共有的属性和方法。保存在原型上面的属性和方法称为公有属性或公有方法。

1. 所以改造 Teacher 类型

  1. function Teacher(name, age, subject, from) {
  2. this.name = name;
  3. this.mission = '传道授业解惑';
  4. this.age = age;
  5. this.subject = subject;
  6. this.from = from;
  7. }
  8. Teacher.prototype.teach = function () {
  9. console.log(`${this.name} 老师教 ${this.subject} 学科`);
  10. };
  11. console.log(Teacher.prototype);
  12. let t1 = new Teacher('mabin', 18, 'js', 'zf');
  13. let t2 = new Teacher('jiangwen', 19, '架构', 'zf');
  14. t1.teach();
  15. t2.teach();
  16. console.log(t1.teach === t2.teach); // true

2. 如何检测属性公有还是私有

  1. // hasOwnProperty() 方法:检测某个属性是否是对象的私有属性,如果是私有属性,返回 true,否则返回false;
  2. let ary = [10,20,30];
  3. console.log('0' in ary); // TRUE
  4. console.log('push' in ary); // TRUE
  5. console.log(ary.hasOwnProperty('0')); // TRUE
  6. console.log(ary.hasOwnProperty('push')); // FALSE "push" 是它公有的属性不是私有的
  7. console.log(Array.prototype.hasOwnProperty('push')); // TRUE 是公有还是私有属性 k,需要看相对
  8. // 谁来说的
  9. console.log(Array.prototype.hasOwnProperty('hasOwnProperty')); // FALSE
  10. console.log(Object.prototype.hasOwnProperty('hasOwnProperty')); // TRUE
  11. //自己堆中有的就是私有属性,需要基于 __proto__ 查找的就是公有属性(__proto__ 在 IE 浏览器中
  12. // (EDGE 除外)给保护起来了,不让我们在代码中操作它)
  13. // “in” :检测这个属性是否属于某个对象(不管是私有属性还是公有属性,只要是它的属性,结果就为 TRUE)

检测某个属性是否为对象的公有属性:hasPubProperty

  1. //基于内置类原型扩展方法
  2. Object.prototype.hasPubProperty = function (property) {
  3. // 验证传递的属性名合法性(一般只能是数字或字符串等基本值)
  4. let x = ["string", "number", "boolean"],
  5. y = typeof property;
  6. if (!x.includes(y)) return false;
  7. // 开始校验是否为公有的属性(方法中的 THIS 就是要校验的对象)
  8. let n = property in this,
  9. m = this.hasOwnProperty(property);
  10. return n && !m;
  11. }
  12. console.log(Array.prototype.hasPubProperty('push')); // FALSE
  13. console.log([].hasPubProperty('push')); // TRUE

二、原型链

  1. function Teacher(name, age, subject, from) {
  2. this.name = name;
  3. this.mission = '传道受业解惑';
  4. this.age = age;
  5. this.subject = subject;
  6. this.from = from;
  7. }
  8. Teacher.prototype.teach = function () {
  9. console.log(`${this.name} 老师教 ${this.subject} 学科`);
  10. };
  11. console.log(Teacher.prototype);
  12. let t1 = new Teacher('mabin', 18, 'js', 'zf');
  13. let t2 = new Teacher('jiangwen', 19, '架构', 'zf');
  14. // 思考? 我把这个方法写在了 Teacher 的原型上,那么 t1 和 t2 是怎么找到的呢?
  15. console.log(t1);
  16. console.log(t2);
  17. console.log(Teacher.prototype);
  18. // 在私有属性中没有发现 teach 方法。我们发现这个 t1 和 t2 中有一个 __proto__ 属性,这个属性值里面有 teach 方法。那是怎么找过去呢? Teacher.prototype 中也有一个 __proto__,这是干嘛呢的?
  19. // 原型链:对象的属性查找机制
  20. // 每个实例都有一个属性 __proto__ 属性,它指向当前实例所属类的 prototype 对象。当我们访对象的一个的属性时,如果有,就使用私有属性,如果没有就通过实例 __proto__ 找到实例所属类的 prototype (原型)上查找,如果找到就使用 prototype 上的属性,如果还没找到,就通过 prototype 的 __proto__ 继续向上查找,一直找到 Object 的 prototype 就停止查找。如果还没找到就返回 undefined。
  21. function Human(name, age, gf) {
  22. this.name = name;
  23. this.age = age;
  24. this.gf = gf;
  25. }
  26. Human.prototype.eat = function () {
  27. console.log('吃')
  28. };
  29. Human.prototype.sleep = function () {
  30. console.log('睡')
  31. };
  32. Human.learn = function () {
  33. console.log('学习')
  34. };
  35. let h = Human('陈*希', 40, '张*芝');
  36. console.log(h.address);

三、原型链查找机制

  1. 先找自己私有的属性,有则调取使用,没有继续找
  2. 基于 proto 找所属类原型上的方法(Fn.prototype),如果还没有则继续基于 proto 往上找…一直找到Object.prototype 为止
  3. 如果找到了就使用,没找到返回 undefined

四、基于内置类的原型扩展方法

  1. /*
  2. * 基于内置类的原型扩展方法
  3. * 在内置类原型上的方法,类所对应的实例可以直接调取使用,例如:实例.方法() ary.push()
  4. * 如果我们也把自己写的方法放到原型上,那么当前类的实例也可以直接这样调取使用了,很方便
  5. *
  6. * 但是也有需要注意的地方
  7. * 1.自己扩展的方法不能影响原有内置的方法(我们自己设置的方法最好加前缀:my)
  8. * 2.扩展方法中的 THIS 一般都是当前类的实例(也就是要操作的值):实例.方法()
  9. */
  10. ~ function () {
  11. /*
  12. * myUnique : 实现数组去重
  13. * @params
  14. * @return
  15. * [Array] 去重后的数组
  16. */
  17. function myUnique() {
  18. // 此时没有传递要操作的 ARY 进来,但是方法中的 THIS 是当前要操作的数组:ARY.MYUNIQUE()
  19. let obj = {};
  20. for (let i = 0; i < this.length; i++) {
  21. let item = this[i];
  22. if (typeof obj[item] !== 'undefined') {
  23. this[i] = this[this.length - 1];
  24. this.length--;
  25. i--;
  26. continue;
  27. }
  28. obj[item] = item;
  29. }
  30. obj = null;
  31. // 保证当前方法执行完返回的结果依然是 ARRAY 类的一个实例
  32. return this;
  33. }
  34. // 扩展到内置类的原型上
  35. Array.prototype.myUnique = myUnique;
  36. }();
  37. let ary = [12, 23, 13, 12, 23, 24, 34, 13, 23];
  38. // ary.myUnique(); 返回去重后的数组(也是 ARRAY 类的实例)
  39. // ary.sort((a, b) => a - b); 返回排序后的数组
  40. // 链式写法(保证返回值依然是当前类的实例 一般都会 RETURN THIS)
  41. // ary.myUnique().sort((a, b) => a - b).reverse().slice(2).push('珠峰').concat(12);
  42. // Uncaught TypeError: ary.myUnique(...).sort(...).reverse(...).slice(...).push(...).concat is not a function 执行完 push 返回的是一个数字(新增后数组的长度),不是数组了,不能在继续使用数
  43. // 组的方法
  44. ary.myUnique().sort((a, b) => a - b).reverse();
  45. console.log(ary);