👇 内容速览 👇javascript
题目:ES5中经常使用继承方法。
方法一:绑定构造函数前端
缺点:不能继承父类原型方法/属性java
function Animal(){ this.species = '动物' } function Cat(){ // 执行父类的构造方法, 上下文为实例对象 Animal.apply(this, arguments) } /** * 测试代码 */ var cat = new Cat() console.log(cat.species) // output: 动物
方法二:原型链继承node
缺点:没法向父类构造函数中传递参数;子类原型链上定义的方法有前后顺序问题。webpack
注意:js中交换原型链,均须要修复prototype.constructor
指向问题。css3
function Animal(species){ this.species = species } Animal.prototype.func = function(){ console.log('Animal') } function Cat(){} /** * func方法是无效的, 由于后面原型链被从新指向了Animal实例 */ Cat.prototype.func = function() { console.log('Cat') } Cat.prototype = new Animal() Cat.prototype.constructor = Cat // 修复: 将Cat.prototype.constructor从新指向自己 /** * 测试代码 */ var cat = new Cat() cat.func() // output: Animal console.log(cat.species) // undefined
方法3:组合继承git
结合绑定构造函数和原型链继承2种方式,缺点是:调用了2次父类的构造函数。es6
function Animal(species){ this.species = species } Animal.prototype.func = function(){ console.log('Animal') } function Cat(){ Animal.apply(this, arguments) } Cat.prototype = new Animal() Cat.prototype.constructor = Cat /** * 测试代码 */ var cat = new Cat('cat') cat.func() // output: Animal console.log(cat.species) // output: cat
方法4:寄生组合继承github
改进了组合继承的缺点,只须要调用1次父类的构造函数。它是引用类型最理想的继承范式。(引自:《JavaScript高级程序设计》)
/** * 寄生组合继承的核心代码 * @param {Function} sub 子类 * @param {Function} parent 父类 */ function inheritPrototype(sub, parent) { // 拿到父类的原型 var prototype = Object(parent.prototype) // 改变constructor指向 prototype.constructor = sub // 父类原型赋给子类 sub.prototype = prototype } function Animal(species){ this.species = species } Animal.prototype.func = function(){ console.log('Animal') } function Cat(){ Animal.apply(this, arguments) // 只调用了1次构造函数 } inheritPrototype(Cat, Animal) /** * 测试代码 */ var cat = new Cat('cat') cat.func() // output: Animal console.log(cat.species) // output: cat
__proto__
属性,__proto__
属性值指向它的构造函数的prototype属性值注:ES6的箭头函数没有prototype
属性,可是有__proto__
属性。
const obj = {}; // 引用类型的 __proto__ 属性值指向它的构造函数的 prototype 属性值 console.log(obj.__proto__ === Object.prototype); // output: true
题目:如何JS中的原型?
// 构造函数 function Foo(name, age) { this.name = name } Foo.prototype.alertName = function () { alert(this.name) } // 建立示例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name) } // 测试 f.printName() f.alertName()
可是执行alertName
时发生了什么?这里再记住一个重点 当试图获得一个对象的某个属性时,若是这个对象自己没有这个属性,那么会去它的__proto__
(即它的构造函数的prototype
)中寻找,所以f.alertName
就会找到Foo.prototype.alertName
。
题目:如何JS中的原型链?
以上一题为基础,若是调用f.toString()
。
f
试图从__proto__
中寻找(即Foo.prototype
),仍是没找到toString()
方法。f.__proto__.__proto__
中寻找(即Foo.prototype.__proto__
中)。由于Foo.prototype
就是一个普通对象,所以Foo.prototype.__proto__ = Object.prototype
Object.prototype.toString
这是对深度遍历的过程,寻找的依据就是一个链式结构,因此叫作“原型链”。
题目:如何理解 JS 的做用域和做用域链。
①做用域
ES5有”全局做用域“和”函数做用域“。ES6的let
和const
使得JS用了”块级做用域“。
为了解决ES5的全局冲突,通常都是闭包编写:(function(){ ... })()
。将变量封装到函数做用域。
②做用域链
当前做用域没有找到定义,继续向父级做用域寻找,直至全局做用域。这种层级关系,就是做用域链。
题目:讲解下面代码的执行过程和结果。
var a = true; setTimeout(function(){ a = false; }, 100) while(a){ console.log('while执行了') }
这段代码会一直执行而且输出"while..."。JS是单线程的,先跑执行栈里的同步任务,而后再跑任务队列的异步任务。
题目:说一下JS的Event Loop。
简单总结以下:
Loop
。题目:解释下“全局执行上下文“和“函数执行上下文”。
①全局执行上下文
解析JS时候,建立一个 全局执行上下文 环境。把代码中即将执行的(内部函数的不算,由于你不知道函数什么时候执行)变量、函数声明都拿出来。未赋值的变量就是undefined
。
下面这段代码输出:undefined
;而不是抛出Error
。由于在解析JS的时候,变量a已经存入了全局执行上下文中了。
console.log(a); var a = 1;
②函数执行上下文
和全局执行上下文差很少,可是多了this
和arguments
和参数。
在JS中,this
是关键字,它做为内置变量,其值是在执行的时候肯定(不是定义的时候肯定)。
题目:解释下js的闭包
直接上MDN的解释:闭包是函数和声明该函数的词法环境的组合。
而在JavaScript中,函数是被做为一级对象使用的,它既能够本看成值返回,还能够看成参数传递。理解了:“Js中的函数运行在它们被定义的做用域,而不是它们被执行的做用域”(摘自《JavaScript语言精粹》) 这句话便可。
题目:闭包优缺点
闭包封住了变量做用域,有效地防止了全局污染;但同时,它也存在内存泄漏的风险:
解决方法是显式对外暴露一个接口,专门用以清理变量:
function mockData() { const mem = {} return { clear: () => mem = null, // 显式暴露清理接口 get: (page) => { if(page in mem) { return mem[page] } mem[page] = Math.random() } } }
《前端知识体系》
《设计模式手册》
《Webpack4渐进式教程》