本周面试题一览:javascript
本周是继承专题,在开始以前,须要先了解构造函数、原型和原型链的相关知识。java
更多优质文章可戳: github.com/YvetteLau/B…git
构造函数和普通函数的区别仅在于调用它们的方式不一样,任何函数,只要经过 new
操做符来调用,那它就能够做为构造函数;任何函数,若是不经过 new
操做符来调用,那么它就是一个普通函数。es6
实例拥有 constructor(构造函数)
属性,该属性返回建立实例对象的构造函数。github
有一点须要说明的是,除了基本数据类型的 constructor
外( null
和 undefined
无 constructor
属性),constructor
属性是能够被重写的。所以检测对象类型时,instanceof
操做符比 constructor
更可靠一些。面试
咱们建立的每一个函数都有 prototype
属性,这个属性指向函数的原型对象。原型对象的用途是包含能够由特定类型的全部实例共享的属性和方法。函数
在默认状况下,全部原型对象都会自动得到一个 constructor
属性,这个属性包含一个指向 prototype
属性所在函数的指针。ui
当调用构造函数建立一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型对象(能够经过实例的 __proto__
来访问构造函数的原型对象)。this
实例.__proto__ === 构造函数.prototype
spa
console.log(Object.prototype.__proto__ === null) //true
console.log(Object.__proto__ === Function.prototype) //true
console.log(Function.prototype.__proto__ === Object.prototype) //true
复制代码
简单回顾一下构造函数、原型和实例的关系:
每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个能够指向原型对象的内部指针(能够经过 __proto
访问)。
假如咱们让原型对象等于另外一个类型的实例,那么此时原型对象包含一个指向另外一个原型的指针,相应地,另外一个原型中也包含着一个指向另外一个构造函数的指针。假如另外一个原型又是另外一个类型的实例,那么上述关系仍然成立,如此层层递进,就构成了实例与原型的链条,这就是原型链的基本概念。
一图胜万言:
调用 instance.getType()
会调用如下的搜索步骤:
instance
实例SimType.prototype
SubType.prototype
SuperType.prototype
,找到了 getType
方法在找不到属性或方法的状况下,搜索过程老是要一环一环地前行到原型链的末端才会停下来。
全部引用类型都继承了 Object
,这个继承也是经过原型链实现的。若是在 SuperType.prototype
尚未找到 getType
,就会到 Object.prototype
中找(图中少画了一环)。
原型链继承的基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。
如 SubType.prototype = new SuperType()
;
能够看出 colors
属性会被全部的实例共享(instance一、instance二、...)。
缺点:
借用构造函数的技术,其基本思想为:
在子类型的构造函数中调用超类型构造函数。
优势:
缺点:
组合继承指的是将原型链和借用构造函数技术组合到一块,从而发挥两者之长的一种继承模式。基本思路:
使用原型链实现对原型属性和方法的继承,经过借用构造函数来实现对实例属性的继承,既经过在原型上定义方法来实现了函数复用,又保证了每一个实例都有本身的属性。
缺点:
优势:
原型继承的基本思想:
借助原型能够基于已有的对象建立新对象,同时还没必要所以建立自定义类型。
在 object()
函数内部,先穿甲一个临时性的构造函数,而后将传入的对象做为这个构造函数的原型,最后返回了这个临时类型的一个新实例,从本质上讲,object()
对传入的对象执行了一次浅拷贝。
ECMAScript5经过新增 Object.create()
方法规范了原型式继承。这个方法接收两个参数:一个用做新对象原型的对象和(可选的)一个为新对象定义额外属性的对象(能够覆盖原型对象上的同名属性),在传入一个参数的状况下,Object.create()
和 object()
方法的行为相同。
在没有必要建立构造函数,仅让一个对象与另外一个对象保持类似的状况下,原型式继承是能够胜任的。
缺点:
同原型链实现继承同样,包含引用类型值的属性会被全部实例共享。
寄生式继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式相似,即建立一个仅用于封装继承过程的函数,该函数在内部已某种方式来加强对象,最后再像真地是它作了全部工做同样返回对象。
基于 person
返回了一个新对象 -—— person2
,新对象不只具备 person
的全部属性和方法,并且还有本身的 sayHi()
方法。在考虑对象而不是自定义类型和构造函数的状况下,寄生式继承也是一种有用的模式。
缺点:
所谓寄生组合式继承,即经过借用构造函数来继承属性,经过原型链的混成形式来继承方法,基本思路:
没必要为了指定子类型的原型而调用超类型的构造函数,咱们须要的仅是超类型原型的一个副本,本质上就是使用寄生式继承来继承超类型的原型,而后再将结果指定给子类型的原型。寄生组合式继承的基本模式以下所示:
constructor
属性至此,咱们就能够经过调用 inheritPrototype
来替换为子类型原型赋值的语句:
优势:
只调用了一次超类构造函数,效率更高。避免在SuberType.prototype
上面建立没必要要的、多余的属性,与其同时,原型链还能保持不变。
所以寄生组合继承是引用类型最理性的继承范式。
Class
能够经过extends关键字实现继承,如:
对于ES6的 class
须要作如下几点说明:
console.log(typeof SuperType);//function
console.log(SuperType === SuperType.prototype.constructor); //true
复制代码
Object.keys(SuperType.prototype);
复制代码
constructor
方法是类的默认方法,经过 new
命令生成对象实例时,自动调用该方法。一个类必须有constructor
方法,若是没有显式定义,一个空的 constructor
方法会被默认添加。
Class
不能像构造函数那样直接调用,会抛出错误。
使用 extends
关键字实现继承,有一点须要特别说明:
constructor
中调用 super
方法,不然新建实例时会报错。若是没有子类没有定义 constructor
方法,那么这个方法会被默认添加。在子类的构造函数中,只有调用 super
以后,才能使用 this
关键字,不然报错。这是由于子类实例的构建,基于父类实例,只有super方法才能调用父类实例。[1] [JavaScript高级程序设计第六章]
[2] ES6 Class
谢谢各位小伙伴愿意花费宝贵的时间阅读本文,若是本文给了您一点帮助或者是启发,请不要吝啬你的赞和Star,您的确定是我前进的最大动力。 github.com/YvetteLau/B…
本文中的代码使用了图片,若是您想直接复制代码,请移步 【Step-By-Step】高频面试题深刻解析 / 周刊06