原型链的串联及扩展
原型
原型:每一个对象都从原型继承属性。每个对象都有 proto 属性 , 每个对象都有原对象,但只有函数对象才有 prototype 属性, 但是除却 function.prototype,function.prototype 也是函数对象,但是没有 prototype。可以使用 p.isPrototype(o) 来检查 p 是否是 o 的原型。
原型模式
- proto、 constructor 属性是对象所独有的;
- prototype 属性是函数独有的;
- 上面说过 js 中函数也是对象的一种,那么函数同样也有属性proto、 constructor;
1 | function a() {} |
原型模式是 js 对继承的一种实现
prototype:构造函数中的属性,指向该构造函数的原型对象。
constructor :原型对象中的属性,指向该原型对象的构造函数
_proto_:实例中的属性,指向 new 这个实例的构造函数的原型对象,对象可以通过
_proto_
来寻找不属于该对象的属性,_proto_
将对象连接起来组成原型链。所有引用类型的proto属性值均指向它的构造函数的 prototype 的属性值。当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去他的proto(即它的构造函数的 prototype)中寻找
prototype 属性的引入
prototype 是一个对象
每一个 new 出的实例都有自己的属性和方法的副本,无法做到属性、方法共享,因此 Brendan Eich 决定为构造函数设置一个 prototype 属性。
这个对象包含一个对象(以下简称 “prototype 对象 ”),所有实例对象需要共享的属性及方法,都放在这个对象里面,那些不需要共享的属性及方法,就放在构造函数里面。
实例对象一旦创建,就自动引用 prototype 对象的属性和方法。也就是说。实例对象的属性和方法,分成两种,一种是本地的,一种是引用的。
1 | function DOG(name) { |
现在,species 属性放在 prototype 对象里,是两个实例对象共享的。只要修改了 prototype 对象,就会同时影响到两个实例对象。
一张图读懂完整的原型链关系**
原型链的作用:对象属性的访问修改和删除。
- 访问。优先在对象本身查找,没有则顺着原型链向上查找
- 修改。只能修改跟删除自身属性,不会影响到原型链上的其他对象。
constructor
constructor 属性获取创造自己的构造函数
1 | var Parent = function () {} |
proto (原型指针)
_proto** 和 prototype 的关系是 **proto__ 是 prototype 的属性
1 | function Person(name, age, job) { |
JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做 proto 的内置属性,用于指向创建它的构造函数的原型对象 ,原型链继承是通过 proto 这个原型指针来完成的
原型链图
褐色的线为原型链
够造函数、原型和实例的关系:
- 每个构造函数都有一个原型对象(x.prototype)
- 原型对象都包含一个指向构造函数的指针(x.prototype.constructor === x)
- 实例都包含一个指向原型对象的内部指针(a.proto )
** 所有函数的默认原型都是 Object 的实例**
instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上——MDN
instanceof 表示某个变量是否是某个对象的实例
1 | function Car(make, model, year) { |
instanceof 可以正确的判断对象类型,因为内部机制是通过对象的原型链中是不是能找到类型的 prototype
1 | console.log([] instanceof Arrage) //true |
null instanceof Object 为 false
1 | console.log(typeof null) //Object |
这是历史遗留的问题,对于 null 值,typeof null 是出了 BUG,但是本质上 null 和 Object 不是同一个类型,null 值并不是以 Object 为原型创建的,恰恰相反Object.prototype.__proto__ = null
instanceof 的底层实现原理
1 | function instanceof(l, r) { |
instanceof 与 constructor 的区别
1 | console.log((1).constructor === Number) //true |
Object.prototype.toString.call
使用 Object 对象的原型方法 toString,返回值是[object 类型]字符串,该方法基本上能判断所有的数据类型.
1 | var toString = Object.prototype.toString |
特殊的箭头函数
箭头函数没有自己的 this,arguments,super 或 new.target。箭头函数表达式更实用于那些需要匿名函数的地方,并且它不能用作构造函数,和 new 一起用会抛出错误,箭头函数没有 prototype 属性。
箭头函数是有-proto-
属性的,所以箭头函数本身是存在原型链的,他也是有自己的构造函数的,但是因为没有 prototype 属性,他的实例-proto-
没法指向,所以箭头函数也就无法作为构造函数。
同时箭头函数由于没有 this 指针,通过 call()和 apply 方法调用一个函数时,只能传递参数,不能绑定 this。
1 | var a = () => { |