We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
最近学习前端知识看到一个大佬写的 blog 感觉特别的棒,于是在阅读和学习之后打算总结(抄 😂 )到自己的知识体系中,建议直接看大佬的总结
JavaScript 中除了基础数据类型外的数据类型都是对象(引用类型),没有类(class),为了实现类似继承以便复用代码的能力,JavaScript 采用了原型和原型链的方式。虽然 ES6 提供了 class 关键字构造类,但其实只是语法糖而已,本质上仍然是一个对象。ES6 实现的继承,本质上仍然是基于原型和原型链。
class
prototype
__proto__
首先,我们需要正确的理解原型、prototype、__proto__之间的关系
null
我们通过下面的代码验证一下以上总结
var a = function(){}; var b = [1, 2, 3]; //函数才有 prototype 属性 console.log(a.prototype); // >> {constructor: ƒ} //非函数没有 prototype 属性 console.log(b.prototype); // >> undefined //a 的构造函数是「Function 函数」,b 的构造函数是「Array 函数」 console.log(a.constructor); // >> ƒ Function() { [native code] } console.log(b.constructor); // >> ƒ Array() { [native code] } //根据我们上面的总结,a、b 对象的原型(__proto__)分别指向 Function、Array 的 prototype 属性 console.log(a.__proto__ === Function.prototype); // >> true console.log(b.__proto__ === Array.prototype); // >> true //同时「Function 函数」和「Array 函数」又都是对象,其构造函数是「Object 函数」,所以 a 和 b 的原型的原型都是 Object.prototype console.log(a.__proto__.__proto__ === Object.prototype); // >> true console.log(b.__proto__.__proto__ === Object.prototype); // >> true //「Object 函数」作为顶级对象的构造函数,它的实例的原型本身就不再有原型了,因此它原型的 __proto__ 属性为 null console.log(new Object().__proto__.__proto__); // >> null //也即 Object 类型对象,其原型(Object.prototype)的 __proto__ 指向 null console.log(Object.prototype.__proto__); // >> null
对象、构造函数和原型三者关系图如下:
使用最新的方法 Object.setPrototypeOf 可以很方便地给对象设置原型,这个对象会继承改原型所有属性和方法; 但是,setPrototypeOf 的性能很差,我们应该尽量使用 Object.create() 来为某个对象设置原型
Object.setPrototypeOf
setPrototypeOf
Object.create()
var obj={ methodA(){ console.log("coffe"); } } //obj 的原型是 Object.prototype console.log(obj.__proto__ === Object.prototype); // >> true var newObj = Object.create(obj); //以 obj 为原型创建一个新的对象 //newObj 继承了它的原型对象 obj 的属性和方法 newObj.methodA(); // >> coffe
当我们访问某个对象的方法或者属性,如果该对象上没有该属性或者方法,JS 引擎就会遍历原型链上的每一个原型对象,在这些原型对象里面查找该属性或方法,直到找到为止,若遍历了整个原型链仍然找不到,则报错
var obj={ methodA(){ console.log("coffe"); } } var newObj = Object.create(obj); //以obj为原型创建一个新的对象 newObj.hasOwnProperty("methodA"); //>> false
上面的代码中,hasOwnProperty 方法并未在 newObj 上定义,也没有在它的原型 obj 上定义,是它原型链上原型 Object.prototype 的方法。其原型链查找顺序如下图所示:
hasOwnProperty
newObj
obj
Object.prototype
ES6之后,增加的 class 本质上是构造函数的语法糖。我们可以通过如下代码进行演示:
class A { } typeof A; // >> "function" A.prototype; // >> {constructor: ƒ}
通过上面的代码,可以发现 class 本质上也是函数,类的 prototype 是一个对象,包含有 constructor 属性。这和函数的 prototype 属性表现具有一致性。 而且,类的所有方法都定义在类的 prototype 属性上面。可以看如下代码:
constructor
class A { constructor() { // ... } toString() { // ... } toValue() { // ... } } console.log(A.prototype); // {constructor: ƒ, toString: ƒ, toValue: ƒ}
class 作为构造函数的语法糖,同时有 prototype 属性和 __proto__ 属性,因此同时存在两条继承链
class A { } class B extends A { } B.__proto__ === A; //>> true B.prototype.__proto__ === A.prototype; //>> true
The text was updated successfully, but these errors were encountered:
No branches or pull requests
JavaScript 中除了基础数据类型外的数据类型都是对象(引用类型),没有类(class),为了实现类似继承以便复用代码的能力,JavaScript 采用了原型和原型链的方式。虽然 ES6 提供了
class
关键字构造类,但其实只是语法糖而已,本质上仍然是一个对象。ES6 实现的继承,本质上仍然是基于原型和原型链。原型、
prototype
、__proto__
首先,我们需要正确的理解原型、prototype、__proto__之间的关系
prototype
是函数的一个属性而已,也是一个对象;它和原型没有绝对的关系。JavaScript 里函数也是一种对象,每个对象都有一个原型,但不是所有对象都有prototype
属性,实际上只有函数才有这个属性__proto__
,指向它的构造函数(constructor)的prototype
属性prototype
属性的值,因此__proto__
也即原型的代名词__proto__
也有自己的__proto__
,层层向上,直到__proto__
为null
。这种由原型层层链接起来的数据结构为原型链,因为null
不再有原型,所以原型链的末端是null
我们通过下面的代码验证一下以上总结
对象、构造函数和原型三者关系图如下:
原型继承
使用最新的方法
Object.setPrototypeOf
可以很方便地给对象设置原型,这个对象会继承改原型所有属性和方法;但是,
setPrototypeOf
的性能很差,我们应该尽量使用Object.create()
来为某个对象设置原型原型链的查找机制
当我们访问某个对象的方法或者属性,如果该对象上没有该属性或者方法,JS 引擎就会遍历原型链上的每一个原型对象,在这些原型对象里面查找该属性或方法,直到找到为止,若遍历了整个原型链仍然找不到,则报错
上面的代码中,
hasOwnProperty
方法并未在newObj
上定义,也没有在它的原型obj
上定义,是它原型链上原型Object.prototype
的方法。其原型链查找顺序如下图所示:类(class)的
prototype
和__proto__
ES6之后,增加的 class 本质上是构造函数的语法糖。我们可以通过如下代码进行演示:
通过上面的代码,可以发现 class 本质上也是函数,类的 prototype 是一个对象,包含有
constructor
属性。这和函数的 prototype 属性表现具有一致性。而且,类的所有方法都定义在类的
prototype
属性上面。可以看如下代码:class 作为构造函数的语法糖,同时有
prototype
属性和__proto__
属性,因此同时存在两条继承链__proto__
属性,表示构造函数的继承,总是指向父类prototype
属性的__proto__
属性,表示方法的继承,总是指向父类的prototype
属性The text was updated successfully, but these errors were encountered: