原文 developer.mozilla.org/en-US/docs/…
JS并不提供类的实现(class关键字是在ES2015引入的但仅是语法糖,JS自己还是基于原型的)。javascript
当谈到继承,JS仅有一种结构:对象。每个对象(object)都有一个指向其余对象的私有属性,这个属性叫作这个对象的原型(prototype)。java
几乎全部的对象都是Object的实例,也能够说全部对象都继承自Object,它是原型链的顶端。Object.protorype指向null,null是没有prototype的。浏览器
当咱们去访问某一个对象的属性时,JS会今后对象沿着原型链往上找,直到找不到为止。bash
----------如下重点---------最难理解的部分---------markdown
obj.[[Prototype]]是ES标准的记法,obj.__proto__是浏览器厂商实际使用的记法,Object.getPrototypeOf(obj)/Object.setPrototypeOf(child, parent) 是ES6提供的访问器,这三种写法本质上是同一种东西。函数
someFunction.prototype跟上面的不是一种东西,它是一种抽象的泛指,表示全部用someFunction看成构造器创造出来的实例的__proto__。他俩的关系相似于class和instance的关系。oop
若是你仔细观察的话,会发现.prototype老是跟在function后面,.__proto__老是跟在创造出的对象后:性能
function doSomething(){} doSomething.prototype.foo = "bar"; console.log( doSomething.prototype );复制代码
结果: { foo: "bar", constructor: ƒ doSomething(), __proto__: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }复制代码
当建立实例后优化
var doSomeInstancing = new doSomething(); doSomeInstancing.prop = "some value"; // add a property onto the object console.log( doSomeInstancing );复制代码
结果this
{ prop: "some value", __proto__: { foo: "bar", constructor: ƒ doSomething(), __proto__: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }复制代码
你能够看到,若是你找doSomeInstancing.__proto__,他指向的是doSomething.prototype。因此当你定义一个方法或类的时候,加在prototype上的属性会变成之后这个类的实例(或后代)的原型链__proto__上的属性。
实例化或者继承在JS中是同样的存在,都会是创造一个新的对象而后让他加入原型链。
当你尝试去在某对象中找属性,JS就会从这个对象开始遍历原型链直到最顶端,因此说继承的越多(原型链越长)性能越差。
当写任何一个东西的时候,JS会自动帮你添加原型链,这就是为何说几乎全部的对象都是Object的实例。除非你用var freshObj = Object.create(null);创造出的就是没有继承Object的
var o = {a: 1}; // The newly created object o has Object.prototype as its [[Prototype]] // o has no own property named 'hasOwnProperty' // hasOwnProperty is an own property of Object.prototype. // So o inherits hasOwnProperty from Object.prototype // Object.prototype has null as its prototype. // o ---> Object.prototype ---> null // 再次提醒:上面体现的是o.__proto__ === Object.prototype, 而不是o.prototype,这玩意是undefined var b = ['yo', 'whadup', '?']; // Arrays inherit from Array.prototype // (which has methods indexOf, forEach, etc.) // The prototype chain looks like: // b ---> Array.prototype ---> Object.prototype ---> null // 同理你在用Array的实例,上面语法实际上是new Array('yo', 'whadup', '?') function f() { return 2; } // Functions inherit from Function.prototype // (which has methods call, bind, etc.) // f ---> Function.prototype ---> Object.prototype ---> null // 这里在定义一个类或方法,因此上面是f.prototype === Function.prototype复制代码
须要用hasOwnProperty()检查某属性是否在这个对象自己定义的。
1. new
function foo(){} foo.prototype = { foo_prop: "foo val" }; function bar(){} var proto = new foo; proto.bar_prop = "bar val"; bar.prototype = proto; var inst = new bar; console.log(inst.foo_prop); console.log(inst.bar_prop);复制代码
优:浏览器支持最广(除了ie5.5以前的),很是快,很是标准。
缺:new的函数必须是初始化过的。由于初始化时,他的构造器可能会存储每一个对象必须生成的独特的信息,然而这个信息只会生成一次,因此会致使潜在的问题。
2. Object.create()
版本1: function foo(){} foo.prototype = { foo_prop: "foo val" }; function bar(){} var proto = Object.create( foo.prototype ); proto.bar_prop = "bar val"; bar.prototype = proto; var inst = new bar; console.log(inst.foo_prop); console.log(inst.bar_prop);复制代码
版本2:使用create的第二个参数设置属性 function foo(){} foo.prototype = { foo_prop: "foo val" }; function bar(){} var proto = Object.create( foo.prototype, { bar_prop: { value: "bar val" } } ); bar.prototype = proto; var inst = new bar; console.log(inst.foo_prop); console.log(inst.bar_prop)复制代码
优:大部分浏览器支持(除ie9以前)。容许直接设置__proto__,由于是一次性设置的因此浏览器能作相应的优化。能创造不继承Object的对象。
缺:若是添加了第二个参数的话,对象初始化过程性能会变不好。由于每一个对象描述器(object-descriptor)属性有它本身的描述器(descriptor)对象。
3. Object.setPrototypeOf()
版本1:第一个参数就是child function foo(){} foo.prototype = { foo_prop: "foo val" }; function bar(){} var proto = { bar_prop: "bar val" }; Object.setPrototypeOf( proto, foo.prototype ); bar.prototype = proto; var inst = new bar; console.log(inst.foo_prop); console.log(inst.bar_prop);复制代码
版本2:返回值是child function foo(){} foo.prototype = { foo_prop: "foo val" }; function bar(){} var proto; proto=Object.setPrototypeOf( { bar_prop: "bar val" }, foo.prototype ); bar.prototype = proto; var inst = new bar; console.log(inst.foo_prop); console.log(inst.bar_prop)复制代码
优:大部分浏览器支持(除ie9以前)。能动态地操做对象原型,甚至强加prototype给无原型的对象(Object.create(null)创造的)。
缺:性能不好。浏览器大多会作对原型的优化且尝试去猜某个方法在内存的位置在你调用某实例以前,而这种动态添加原型的方法基本上毁了他们的优化甚至强迫某些浏览器重编译。
4. .__proto__
function foo(){} foo.prototype = { foo_prop: "foo val" }; function bar(){} var proto = { bar_prop: "bar val", __proto__: foo.prototype }; bar.prototype = proto; var inst = new bar; console.log(inst.foo_prop); console.log(inst.bar_prop);复制代码
版本2:从头到脚直接用proto var inst = { __proto__: { bar_prop: "bar val", __proto__: { foo_prop: "foo val", __proto__: Object.prototype } } }; console.log(inst.foo_prop); console.log(inst.bar_prop)复制代码
优:大部分浏览器支持(除ie11以前)。把__proto__设置成非对象的值会隐式失败,不会抛异常。
缺:已被标准弃用(尽管还有浏览器支持),性能不好。跟上面同样,浏览器大多会作对原型的优化且尝试去猜某个方法在内存的位置在你调用某实例以前,而这种动态添加原型的方法基本上毁了他们的优化甚至强迫某些浏览器重编译。
1. 为何在文档上面提到不要func.prototype = {a: 'a'}这么写,由于会破坏原型链,而下面的好多例子又直接那么写呢?
// add properties in f function's prototype f.prototype.b = 3; f.prototype.c = 4; // do not set the prototype f.prototype = {b:3,c:4}; this will break the prototype chain复制代码
由于若是你有不少层的继承的话,显而易见,你直接让他指向一个其余对象,那么前面的链就断了。但是例子里面都只有一层继承,他们上面都是Object.prototype,因此无所谓了
2. 为何var b = {}; 或 b = []打印出来的b.prototype === undefined?
已经在前面讲过了,对象(实例)要用__proto__
3. 为啥说b = []的继承链上一层是Array.prototype呢, 从哪看出来的?
打印一下就知道了:
4. class(用class关键字声明的)怎么继承纯obj?
const Animal = { speak() { console.log(this.name + ' makes a noise.'); } }; class Dog { constructor(name) { this.name = name; } } // If you do not do this you will get a TypeError when you invoke speak Object.setPrototypeOf(Dog.prototype, Animal); let d = new Dog('Mitzie'); d.speak(); // Mitzie makes a noise.复制代码