JavaScript闭包、原型链、继承 ——总结

tech2022-07-04  158

面试回答技巧(一) 如何回答的一个技术记汇,或者你对xxxx的理解? 例如:你说一下对闭包的理解? 答:1.xxx是什么? 2.应用场景 3.优缺点 4.具体实现 5.还有没有更好的解决方案!

今天的内容都是从这几个方面来讲解。

一、闭包

1、闭包是什么?

函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在 JavaScript 中,每当函数被创建,就会在函数生成时生成闭包。

总结来说:闭包就是函数加词法作用域。只要符合这两种情况基本上都是闭包。

那么什么是词法作用域呢?这里就来说说,看下面的代码:

function init() { var name = "张三"; // name 是一个被 init 创建的局部变量 function displayName() { // displayName() 是内部函数,一个闭包 alert(name); // 使用了父函数中声明的变量 } displayName(); } init();

init() 创建了一个局部变量 name 和一个名为 displayName() 的函数。displayName() 是定义在 init() 里的内部函数,并且仅在 init()函数体内可用。请注意,displayName() 没有自己的局部变量。然而,因为它可以访问到外部函数的变量,所以displayName() 可以使用父函数 init() 中声明的变量 name 。

总结: 词法作用域:即以变量声明定义的位置为参照,如果当前位置没有定义,就会访问父级定义的位置。

闭包又分为广义和狭义两种:

a、广义的闭包:

var a=1000; function fn1() { alert(a) } fn1()

b、狭义的闭包:

平时工作中用到的闭包是狭义上的闭包:

1.函数内嵌套函数 2.子函数引用了父函数的相关变量

符合上面的条件都是狭义的闭包。

特点:长期驻留内存

2、闭包的应用场景和怎么实现?

//求和 function makeAdd(x) { return function(y) { return x+y } } //设置字号 function setFontSize(size) { return function() { document.body.style.fontSize=size+"px" } } //循环表单 function makeHelp(help) { return function() { console.log(help) document.querySelector('.help').innerHTML=help } } function init() { var userInfo=[ {id:'username',helpText:'请输入用户名'}, {id:'email',helpText:'请输入邮箱'}, {id:'address',helpText:'请输入地址'}, ] //动态绑定onfocus事件 for(var i=0;i<userInfo.length;i++) { var item=userInfo[i]; document.getElementById(item.id).onfocus=makeHelp(item.helpText) } } init() //封装组件或插件 var Counter=(function() { //私有变量 var index=0; //私有方法 var add=function() { return index++; } var jian=function() { } return { //暴露出去供用户的方法 increment:function() { add() }, getValue:function() { return index; } } })()

3、闭包的优缺点

优点:

1.长期驻留内存,可以缓存数据 2.可以隔离作用域,避免全局污染

缺点:

长期使用,会造成内存泄漏。

解决内存泄漏的方法: 链接:https://www.jianshu.com/p/d903be89f211 上面的链接,自我感觉可以。

4、最好的解决方案

使用ES6的let方法,可以有效的避免。

二、原型链

https://www.jianshu.com/p/08c07a953fa0

1、原型链是什么?

每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找… 这个操作被委托在整个原型链上,这个就是我们说的原型链了。

原型链是JS特有的一种继承机制。原型链会涉及到__proto__,prototype。原型链的顶端就是null。

2、原型链的主要应用场景

继承

3、优点和缺点

优点:把相同或类似的方法写在原型上,方便实例化对象复用缺点:不好理解,通常只前端人才理解

4、解决方案

ES6推出class extends来实现继承。

三、JavaScript继承

1、继承是什么?

继承是面向对象开发思想的特性之一。 面向对象的三大特点:封装、继承、多态(多种状态)。

2、实现方式(有两种)

a、ES5继承(主要通过函数来实现类)

原型链继承 //创建一个父类 function Parent() { this.name='jack' } Parent.prototype.getName=function() { return this.name; } //创建一个子类 function Child() { } //子类的原型等于父类的实例化对象 Child.prototype=new Parent(); var c1=new Child()

缺点: 1、不能传参 2、 没有解决对象引用问题

借用构造函数继承 //创建一个父类 function Parent(name) { this.name=name ||'jack' this.colors=['red','green','blue'] } Parent.prototype.getName=function() { return this.name; } //创建一个子类 function Child(name) { //借用父类来承继实例属性,但不能继承父类方法 Person.call(this,name) }

缺点:不能继承父类方法

组合继承(原型链继承 + 借用构造函数) //创建一个父类 function Parent(name) { this.name=name ||'jack' this.colors=['red','green','blue'] } Parent.prototype.getName=function() { return this.name; } var p1=new Parent(); p1.getName(); //创建一个子类 function Child(name) { Parent.call(this,name) } Child.prototype=new Parent(); var c1=new Child() c1.getName()

优点:既能继承父类的原型和方法,也能传递参数属性。

b、ES6继承

通过class,extends,super实现 //继承必须要写super

//创建一个父类 class Parent { constructor(name) { this.name=name ||'jack' this.colors=['red','green','blue'] } getName() { return this.name; } } //创建一个子类 class Child extends Parent { constructor(name) { super(name) //super就是父类Parent } getValue() { } }
最新回复(0)