JavaScript语言不像面向对象的编程语言中有类的概念,因此也就没有类之间直接的继承,JavaScript中只有对象,使用函数模拟类,基于对象之间的原型链来实现继承关系,
ES6的语法中新增了class关键字,但也只是语法糖,内部仍是经过函数和原型链来对类和继承进行实现。编程
JavaScript对象上都有一个内部指针[[Prototype]],指向它的原型对象,而原型对象的内部指针[[Prototype]]也指向它的原型对象,直到原型对象为null,这样造成的链条就称为原型链。数组
这样在访问对象的属性时,会如今本身的属性中查找,若是不存在则会到上一层原型对象中查找。编程语言
注意:
根据 ECMAScript 标准,someObject.[[Prototype]] 符号是用于指派 someObject 的原型。这个等同于 JavaScript 的 proto 属性(现已弃用)。从 ECMAScript 6 开始, [[Prototype]] 能够用Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问。函数
例如:学习
var obj2 = { height: 170 } var obj3 = { name: 'obj3' } Object.setPrototypeOf(obj3, obj2); console.log(obj3.height); // 170 var isproto = Object.getPrototypeOf(obj3) === obj2; console.log(isproto); // true
ECMAScript 5 中引入了一个新方法:Object.create()。能够调用这个方法来建立一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数。this
例如:spa
var a = {a: 1}; // a ---> Object.prototype ---> null var b = Object.create(a); // b ---> a ---> Object.prototype ---> null console.log(b.a); // 1 (继承而来) var c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null var d = Object.create(null); // d ---> null console.log(d.hasOwnProperty); // undefined, 由于d没有继承Object.prototype
在 JavaScript 中,构造函数其实就是一个普通的函数,通常函数名首字母大写。当使用 new 操做符 来做用这个函数时,它就能够被称为构造方法(构造函数)。
例如:prototype
function Person (name, age) { this.name = name; this.age = age; } Person.prototype = { sayName: function () { console.log(this.name); } } var person1 = new Person('yangyiliang', 23); person1.sayName(); // yangyiliang
使用构造函数建立对象,经历了以下三个关键步骤:设计
var temp = {}; //1 建立空对象 Person.call(temp, 'yangyiliang', 23); //2 以空对象为this执行构造函数 Object.setPrototypeOf(temp, Person.prototype); //3 将构造函数的prototype 设置为空对象的原型 return temp;
使用字面量方法建立的对象,根据对象的类型,他们的原型都会指向相应JavaScript内置构造函数的prototype,和直接使用内置构造函数建立对象生成的原型链相同,例如:指针
var o = {a: 1}; // o这个对象继承了Object.prototype上面的全部属性 // 因此能够这样使用 o.hasOwnProperty('a'). // hasOwnProperty 是Object.prototype的自身属性。 // Object.prototype的原型为null。 // 原型链以下: // o ---> Object.prototype ---> null var a = ["yo", "whadup", "?"]; // 数组都继承于Array.prototype // (indexOf, forEach等方法都是从它继承而来). // 原型链以下: // a ---> Array.prototype ---> Object.prototype ---> null function f(){ return 2; } // 函数都继承于Function.prototype // (call, bind等方法都是从它继承而来): // f ---> Function.prototype ---> Object.prototype ---> null
在面向对象的语言当中,继承关系应该指的是父类和子类之间的关系,子类继承父类的属性和方法,在JavaScript当中是父构造函数和子构造函数之间的关系。
类自己是对象的抽象形式,类的使用价值最后也是在于经过它可以建立对象,
因此子类可以继承父类的属性和方法的意义,就是经过子类建立出来的对象可以继承经过父类建立出来的对象的属性和方法。
而这种对象之间的继承关系,就是经过原型链实现。
在1.2.2节中,咱们学习到了经过构造函数建立对象的三个重要步骤,其中的一步是把构造函数的prototype对象设置为建立对象的原型。
所以咱们将父类的实例对象做为子类的prototype即可以达到继承的目的,以下图所示:
继承的实现
function Person (name, age) { this.name = name; this.age = age } Person.prototype.sayName = function () { console.log('my name is ' + this.name); } function Student (name, age, school) { Person.call(this, name, age); this.school = school; } Student.prototype = Object.create(Person.prototype); Student.prototype.saySchool = function () { console.log('my school is ' + this.school); }
上面代码实现的继承,遵循了几个原则:
利用Object.create而不是直接用new 建立一个实例对象的目的是,减小一次调用父构造函数的执行。
最后上一张js高级程序设计第三版中的一张源于原型链继承的图
下面利用ES6引入的新语法糖,class、extends关键字对上述实现继承的代码进行改写:
class Person { constructor (name, age) { this.name = name; this.age = age; } sayName () { console.log('my name is ' + this.name); } } class Student extends Person { constructor (name, age, school) { super(name, age); this.school = school; } saySchool () { console.log('my school is ' + this.school); } }
本文部份内容来自 https://developer.mozilla.org...
后续
function A() { } //函数默认会有一个prototype对象而且具备constructor属性指向他自己 var a = new A() a instanceof A
function A() { } function B() { } var proto = {} B.prototype = proto A.prototype = proto var a = new A() a instanceof B //true a instanceof Object //true instanceof 是遍历a 的原型链 寻找是否有和 B.prototype 是同一个对象的__proto__ 若是找到就为true