当谈到继承时,JavaScript 只有一种结构:对象。每一个实例对象(object )都有一个私有属性(称之为proto)指向它的原型对象(prototype)。该原型对象也有一个本身的原型对象(proto) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并做为这个原型链中的最后一个环节。数组
新建函数,并建立对象浏览器
function Car() { this.name = 'BMW' this.price = 95800 } let carBMW = new Car()
这时咱们的脑海里应该有这样一张图:函数
或许你跟我初次接触同样。若是对该图不怎么理解,不要着急,继续往下看!!!this
JavaScript 对象是动态的属性“包”(指其本身的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不单单在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。spa
从 ECMAScript 6 开始,[[Prototype]] 能够经过Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 __proto__。
接着上述代码prototype
console.log(carBMW) // *Car {name: "BMW", price: 95800}*
在 Car() 函数的原型上定义属性code
Car.prototype.price = 200000 Car.prototype.speed = 300 console.log(carBMW.__proto__) // {price: 200000, speed: 300, constructor: ƒ} console.log(carBMW.__proto__.__proto__ == Object.prototype) // true console.log(carBMW.__proto__.__proto__.__proto__) // null
综合上述代码,能够给出以下原型链对象
{name: "BMW", price: 95800} ---> {price: 200000, speed: 300, constructor: ƒ} ---> Object.prototype ---> null
继续写代码blog
console.log(carBMW.name) // BMW // name 为 carBMW 自身的属性 console.log(carBMW.price) // 95800 // price 为 carBMW 自身的属性,原型上也有一个'price'属性,可是不会被访问到,这种状况称为"属性遮蔽 (property shadowing)" console.log(carBMW.speed) // 300 // speed 不是 carBMW 自身的属性,可是 speed 位于该原型链上,所以咱们依然能够取到该值 // 固然若是你试着访问一个不存在原型链上的属性时,这时候会给你返回一个undefined
当咱们给对象设置一个属性时,建立的属性称为对象的自有属性。继承
函数的继承与其余的属性继承没有差异,包括上面的“属性遮蔽”(这种状况至关于其余语言的方法重写)。
当继承的函数被调用时,this 指向的是当前继承的对象,而不是继承的函数所在的原型对象。
let car = { price: 95800, getPrice: function(){ return this.a } } console.log(car.getPrice()); // 95800 // 当调用 car.getPrice() 时,'this'指向了car. let bmw = Object.create(car); // bmw 是一个继承自 car 的对象 bmw.price = 400000; // 建立 bmw 的自身属性 price console.log(bmw.getPrice()); // 400000 // 调用 bmw.getPrice() 时, 'this'指向 bmw. // 又由于 bmw 继承 car 的 getPrice 函数 // 此时的'this.price' 即 bmw.a,即 price 的自身属性 'price'
虽然有点绕,细读以后逻辑并非很复杂
也就是根据相应的语法结构直接进行建立
let car = { price: 95800, getPrice: function(){ return this.a } } // car 为一个对象,所以相应的原型链应该以下 // car ---> Object.prototype ---> null let cars = ['BMW','Audi','WulingHongguang'] // cars 为一个数组对象,相应的原型链应该以下 // cars ---> Array.prototype ---> Object.prototype ---> null
在 JavaScript 中,构造器其实就是一个普通的函数。当使用 new 操做符 来做用这个函数时,它就能够被称为构造方法(构造函数)。
function Car() { this.name = 'BMW' this.price = 95800 } Car.prototype.speed = 300 let car = new Car() // 能够知道,car 的自身属性 {name: "BMW", price: 95800}, 位于原型链上的属性有 speed .
ECMAScript 5 中引入了一个新方法:Object.create()。能够调用这个方法来建立一个新对象。新对象的原型就是调用 create 方法时传入的第一个参数
var car = {price: 10000}; // car ---> Object.prototype ---> null var carBMW = Object.create(car); // carBMW ---> car ---> Object.prototype ---> null console.log(carBMW.price); // 10000 (继承而来)
ECMAScript6 引入了一套新的关键字用来实现 class。实质上仍是语法糖,底层原理依旧不变
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty('x') // true point.hasOwnProperty('y') // true point.hasOwnProperty('toString') // false point.__proto__.hasOwnProperty('toString') // true
以上代码中,x和y都是实例对象point自身的属性(由于定义在this变量上),因此hasOwnProperty方法返回true,而toString是原型对象的属性(由于定义在Point类上),因此hasOwnProperty方法返回false。这些都与ES5的行为保持一致。