原型prototype,在建立新函数的时候,会自动生成,而prototype中也会有一个constructor,回指建立该prototype的函数对象。javascript
__proto__是对象或者实例中内置的[[prototype]],其指向的是产生该对象的对象的prototype,在浏览器中提供了__proto__让咱们能够访问,经过__proto__的指向造成的一个链条,就称作原型链,原型链的整个链路是:实例对象- ->构造函数的prototype- ->Object的prototype- ->null。java
咱们在访问对象的属性或者方法的时候,首先从本对象寻找,若是本对象不存在该属性或者方法时,就会沿着原型链向上寻找,直至找到该属性或者方法,或者到null时中止。es6
这也解释了为何数组对象上没有push,pop,shift,unshift等方法,却能够访问。数组
constructor属性指向的是生成该函数(对象)的函数(对象),例如浏览器
var a = function(){}; var b = new a(); var c = {}; var d = []; //如下皆为true console.log(b.constructor === a) //由于实例b是由构造函数产生的 console.log(a.constructor === Function)//函数a实际是Function的实例,同理 console.log(c.constructor === Object)//空对象c是Object的实例 console.log(d.constructor === Array)//空对象c是Object的实例 console.log(Object.constructor === Function)//Object自身就是一个构造函数,同理 console.log(Array.constructor === Function)//Array自身也是一个构造函数 //--------------------------------------------------------------- //首先__proto__指向的是产生该对象的对象的prototype, //也即a.prototype,prototype中也的constructor,回指建立该prototype的函数对象,也即函数a console.log(b.__proto__.constructor === a)
这里顺便说一下instanceof,A instanceof B 是在 A 的原型链中找 B 的 prototype,找到返回 true,找不到返回 falsebabel
//有个奇怪的现象,下面都返回true,这是为何呢? //由于JS中一切都继承自Object,除了最顶层的null, //因此在Function的原型链中能找到Object.prototype console.log(Function instanceof Object) //而Object自身就是一个构造函数,所以在Object的原型链中也能找到Function.prototype console.log(Object instanceof Function)
由上面的分析,咱们能够利用原型链实现继承的逻辑,继承是面向对象中的一个很重要的概念app
function Dog(name){ this.name = name; this.say1 = function(){ console.log(this.name) } } Dog.prototype.say2 = function(){ console.log(this.name) } Dog.prototype.test = 1 //say原本应该是全部Dog实例的共有方法, //若是放在构造函数中,那么就会致使没办法数据共享,每个实例都有本身的属性和方法的副本,这是对资源的极大浪费 //若是放在Dog.prototype中,那么利用原型链的特性,就可让全部实例共用一个方法, //须要注意的是,因为共用了一个方法,对属性的更改是对全部实例透明的 var dog1 = new Dog('lalala'); let dog2 = new Dog('wahaha'); dog1.test++;//2 dog2.test++;//3 console.log(dog1.say1 === dog2.say1)// false console.log(dog1.say2 === dog2.say2)// true //如今,咱们能够尝试着去实现继承了 //咱们是经过原型链去实现继承的, //以前的原型链是:Dog实例 --> Dog函数 --> Object --> null //那么如今的原型链须要改为 Dog实例 --> Dog函数 --> Dog父类(Animal函数) --> Object --> null //第一种方案,改变Dog函数的prototype,让他指向Animal的实例 function Animal(){ this.species = 'unknown'; } Dog.prototype = new Animal(); //这里改变后会致使prototype中的constructor改变 Dog.prototype.constructor = Dog; //第二钟方案,改变Dog函数的prototype,让他指向Animal的prototype function Animal(){} Animal.prototype.species = 'unknown'; Dog.prototype = Animal.prototype; //这里改变后会致使prototype中的constructor改变 Dog.prototype.constructor = Dog; //第三种方案,调用apply或call,将Animal的this绑定到Dog中 function Animal(){ this.species = 'unknown'; } function Dog(name){ Animal.apply(this, arguments); this.name = name; } //第四种方法,经过Object.create()方法实现继承,过滤掉了父类实例属性,Dog.prototype中就没有了Animal的实例化数据了 //这种方法也是ES6中Class被babel编译成ES5所用的方法 function Animal(){ this.species = 'unknown'; } function Dog(name){ Animal.apply(this, arguments); this.name = name; } //这里模拟了 Dog.prototype = Object.create(Animal.prototype) var f = function(){}; f.prototype = Animal.pototype; Dog.prototype = new f(); Dog.__proto__ = Animal; //这里改变后会致使prototype中的constructor改变 Dog.prototype.constructor = Dog; //如今就能访问到Animal中的species属性了 var dog = new Dog('lalala'); dog.species;//unknown
以上这些就是利用原型链实现继承的一些方法函数
有了以上的知识,咱们就能够研究一下ES6的class类了,这个语法糖能让咱们更容易的实现类和继承,其提供了extends,static,super等关键字this
//这是es6的代码实现 class Parent { static l(){ console.log(222) } constructor(m){ this.m = m } get(){ return this.m; } } class Child extends Parent { constructor(n){ super(4); this.n = n; } get(){ return this.n } set(a){ this.n = a; } } //这是利用babel编译以后的es5的实现 //_createClass是一个自执行函数,做用给构造函数绑定静态方法和动态方法 //对于静态的static关键字声明的变量,会直接绑定在函数对象上,做为静态属性(方法) //对于在class中声明的函数方法,则会绑定在构造函数的prototype上,经过Object.definePropety方法 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); //若是父函数没有返回值或者返回值不为object或者function,则返回子类的this function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } //_inherits就是extends关键字发挥的做用,实现了继承的功能。利用&&的短路特性,对superClass作了容错性处理,而后将子类Object.create()传了两个参数,一个参数是父类superClass.prototype,做用在上面解释继承的方法时讲过了,第二个参数是一个键值对,key表明着属性,value则和Object.definePropety中descriptor同样,这里改变constructor的目的,也在解释继承时讲过了,最后将subClass.__proto__指向superClass function _inherits(subClass, superClass) { //...省略 subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } //_classCallCheck是保证构造函数不能被当成普通函数调用,须要用new关键字 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Parent = function () { _createClass(Parent, null, [{ key: "l", value: function l() { console.log(222); } }]); function Parent(m) { _classCallCheck(this, Parent); this.m = m; } _createClass(Parent, [{ key: "get", value: function get() { return this.m; } }]); return Parent; }(); var Child = function (_Parent) { _inherits(Child, _Parent); function Child(n) { _classCallCheck(this, Child); //因为在_inherits中将subClass(child).__proto__指向了superClass(Parent),因此这里便是Parent.call(this,4),即这里执行的是super函数,super也能够调用父类的静态方法, //若是父函数没有返回值或者返回值不为object或者function,则返回子类的this var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, 4)); _this.n = n; return _this; } _createClass(Child, [{ key: "set", value: function set(a) { this.n = a; } }]); return Child; }(Parent);