原型继承

  • JavaScript 中,所有的对象都有一个隐藏的 [[Prototype]] 属性,它可以是另一个对象或者 null
  • 我们可以使用 obj.__proto__ 进行访问(还有其他方法,但很快就会被覆盖)。
  • [[Prototype]] 引用的对象称为“原型”。
  • 如果我们想要读取 obj 属性或者调用一个方法,而且它不存在,那么 JavaScript 就会尝试在原型中查找它。写/删除直接在对象上进行操作,它们不使用原型(除非属性实际上是一个 setter)。
  • 如果我们调用 obj.method(),而且 method 是从原型中获取的,this 仍然会引用 obj。因此方法重视与当前对象一起工作,即使它们是继承的。

函数原型

当用 new F() 创建一个新对象时,该对象的 [[Prototype]] 被设置为 F.prototype

prototype只出现在构造函数上

  1. obj.__proto__ === Object.prototype

原生的原型

所有的内置原型顶端都是 Object.prototype。有些时候人们说“一切都从对象上继承而来”。

image.png

  • 所有的内置对象都遵循一样的模式:
    • 方法都存储在原型对象上(Array.prototypeObject.prototypeDate.prototype 等)。
    • 对象本身只存储数据(数组元素、对象属性、日期)。
  • 基本数据类型同样在包装对象的原型上存储方法:Number.prototypeString.prototypeBoolean.prototype。只有 undefinednull 没有包装对象。
  • 内置对象的原型可以被修改或者被新的方法填充。但是这样做是不被推荐的。只有当添加一个还没有被 JavaScript 引擎支持的新方法的时候才可能允许这样做。

模拟new

new操作符做了这些事:

  • 它创建了一个全新的对象
  • 它会被执行[[Prototype]](也就是proto)链接
  • 它使this指向新创建的对象
  • 通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上
  • 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用
    1. 新生成了一个对象
    2. 链接到原型
    3. 绑定 this
    4. 返回新对象
  1. // objectFactory(constructor, 'cxk', '18')
  2. function objectFactory() {
  3. // 创建一个空的对象
  4. let obj = new Object()
  5. // 获得构造函数
  6. let Con = [].shift.call(arguments)
  7. // 链接到原型
  8. obj.__proto__ = Con.prototype
  9. // 绑定 this,执行构造函数
  10. let result = Con.apply(obj, arguments)
  11. // 确保 new 出来的是个对象
  12. return typeof result === 'object' ? result : obj
  13. }

模拟Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto

  1. // 模拟 Object.create
  2. function create(proto) {
  3. function F() {}
  4. F.prototype = proto;
  5. return new F();
  6. }

实现instanceOf

instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype

  1. // 模拟 instanceof
  2. function instance_of(L, R) {
  3. //L 表示左表达式,R 表示右表达式
  4. var O = R.prototype; // 取 R 的显示原型
  5. L = L.__proto__; // 取 L 的隐式原型
  6. while (true) {
  7. if (L === null) return false;
  8. if (O === L)
  9. // 这里重点:当 O 严格等于 L 时,返回 true
  10. return true;
  11. L = L.__proto__;
  12. }
  13. }

实现一个call

  • 将函数设为对象的属性
  • 执行&删除这个函数
  • 指定this到函数并传入给定参数执行函数
  • 如果不传入参数,默认指向为 window
    1. Function.prototype.myCall = function (context) {
    2. const context = context || window
    3. // 给 context 添加一个属性
    4. // getValue.call(a, 'yck', '24') => a._func = getValue
    5. context._func = this
    6. // 将 context 后面的参数取出来
    7. const args = [...arguments].slice(1)
    8. // getValue.call(a, 'yck', '24') => a._func('yck', '24')
    9. const result = context._func(...args)
    10. // 删除 _func
    11. delete context._func
    12. return result;
    13. }