本文已参与「新人创作礼」活动,一起开启掘金创作之路。
[来自作者的小声BB:本人是一个初出茅庐的工作小白,刚刚开始接触并且学习前端开发,以下只是本人在学习过程中的一些见解和笔记,若有不正确的地方,还请大家指出=、=,共同学习,共同成长,respect~]
1.闭包
MDN上的定义:一个函数和对其周围状态的引用捆绑在一起,这样的组合就是闭包。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。 闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包最常见的方式是在一个函数内创建另一个函数,通过内部的函数来访问外部函数的局部变量,其本质上就是一个函数。我们都知道,在一个函数被调用后,其内部的变量对象本应该被销毁,但闭包的存在使得我们依然可以访问到外部函数的变量对象,这就是闭包的关键所在。
特点:
- 函数内再嵌套函数
- 内部函数可以引用外部函数的参数和变量
- 可以避免使用全局变量,防止全局变量被污染
- 参数和变量不会被垃圾回收机制回收
缺点:
- 常驻内存,增大内存的消耗,不正当使用的话会造成内存泄露的问题
2.原型链
要想搞懂原型链,我们得先从原型以及prototype、proto、constructor三个属性说起。
原型的概念:每当javaScript对象被创建的时候,他都会随之关联另一个对象,这个关联的对象就是我们所说的原型,每一个javaScript对象都会从它对应的原型中继承一些属性和方法。
①prototype
在JavaScript中,每一个函数都会有一个prototype属性,这个属性指向了该函数的原型对象。当用这个函数构造出一个对象实例时,这个对象实例也就继承了原型对象上的属性,如下:
function Person (){}
Person.protytype.name = '原型'
var person1 = new Person()
console.log(person1.name)
//输出:原型
我们定义了一个构造函数Person,这个构造函数内部是空的,什么也没有,然后使用Person.prototype的方式给他的原型对象上添加一个name属性,接着new一个构造函数的实例对象person1,然后打印person1的name属性。我们在new这个实例对象的时候,并没有给person1一个name属性,那么他就会去构造函数Person中找,但是Person中也没有定义name属性,所以Person会接着去Person.prototype上找,也就是Person的原型上找,最后在Person.prototype上找到了先前我们定义的name属性,这一整个向上不断查找的过程,就是最基本的原型链。
②__proto__
每一个实例对象都有一个__proto__属性,它指向该实例对象的构造函数的原型。
function Person(){}
var person1 = new Person()
console.log (person1.__proto__ === Person.prototype)//true
③constructor
每个原型都有一个constructor属性(也就是说constructor是存在于prototype上的属性),它指向关联的构造函数。
上面我们说到原型的概念时,提到过每个javaScript对象被创建时都会随之关联另一个对象,当我们创建一个构造函数时,这个构造函数的原型上就会有个constructor属性,这个属性就指向该构造函数。(可以想象一下原型与构造函数的对话:你用prototype指向我,我用constructor指向你 0.0)
function Person(){}
var person1 = new Person()
console.log(Person.prototype.constructor === Person) //true
console.log(Person.prototype.constructor === person1.constructor)//true
由于person1本身没有constructor属性,它会沿着原型链向上查找,找到它的构造函数Person,但是Person本身也没有constructor属性,所以会继续向上查找,找到Person的原型,即Person.prototype,它有constructor属性,所以上面上个打印都为true
④原型的原型
原型本质上也是一个对象,对象都可以通过new Object()来构建,那么也就是说,原型的proto属性指向原型的原型,也就是Object.prototype
function Person(){}
var person1 = new Person()
console.log(Person.__proto__ === Function.prototype) //true
console.log(Person.prototype.__proto__ === Object.prototype) //true
原型链
简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型(prototype),而原型都有一个指向该构造函数的指针(constructor),而构造函数的实例内部又包含一个指向原型的指针(proto)。那么,假如我们令原型A等于另一个构造函数B的实例,那么此时这个实例A内部就会有一个指向另一个原型C的指针,相应的,这个原型C中就会有一个指向另一个构造函数D的指针,假如这个原型C又是另一个构造函数的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条,也就是所谓的原型链。
确定原型关系的方法
①instanceof
拿实例去与构造函数进行比较,使用instanceof
function Person(){}
var person1 = new Person()
console.log(person1 instanceof Person) //true
console.log(person1 instanceof Object) //true
②isPrototypeOf
拿构造函数的原型去与实例进行比较,使用isPrototypeOf
function Person(){}
var person1 = new Person()
console.log(Person.prototype.isPrototypeOf(person1)) //true
console.log(Object.prototype.isPrototypeOf(person1)) //true
3.this详解
定义:this代表当前正在执行某个方法的对象,如果没有当前方法(或该方法不属于任何一个其他对象),则是指全局对象window。也就是说,this代表调用该方法对象的引用。
this的指向: this的指向不是固定不变的,是根据调用时的上下文(执行环境)改变而改变的
- 如果单独使用,this指向全局对象
- 在方法中,this指向该方法所属的对象
- 在函数中,this指向全局对象
- 在事件中,this表示接收事件的元素
①全局执行环境
在全局执行环境下直接打印this,this指向的是window
<script>
console.log(this) //window
console.log(this == window) //true
<script>
由上可说明,输出一个变量,可以采用以下几种方式:
var name = '张三'
console.log(name)
console.log(window['name'])
console.log(window.name)
console.log(this['name'])
console.log(this.name)
在全局执行环境下定义一个函数,在函数内部打印this,然后调用这个函数,this指向的是window
<script>
function fn (){
console.log(this)
}
fn() //window
<script>
以全局对象的方法形式调用,this指向window
function fn(){
console.log(this)
}
fn() //window
console.log(this.fn()) //window
console.log(window.fn()) //window
②局部执行环境
以对象的方法形式调用,哪个对象来调用就指向哪个对象
var obj1 = {
name: '张三'
fun1: function(){
console.log(this)
}
obj2: {
fun2: function(){
console.log(this)
}
}
}
obj1.fun1() //obj1 this指向obj1
obj1.obj2.fun2() //obj2 this指向obj2
构造函数中的this指向该构造函数的实例化对象
var Person = function(){
this.name = '张三' //这里的this指向的是将来用new关键字实例化的对象
}
var obj = new Person() //此处实例化一个对象,内部的this就指向obj
事件中的this,在HTML事件中,this指向了接收事件的元素
<button onclick="this.style.display='none'"></button> this指向button这个元素
③改变this的指向:借助call或apply函数来改变this的指向
var obj = {
name: '张三'
}
function fn1(){
console.log(this.name)
}
function fn2(){
console.log(this)
}
fn1.call(obj) //张三 this指向obj
fn1.call(fn2) //function this指向fn2函数
本文完,感谢阅读~