javascript关于闭包、原型链和this的一点小见解~

256 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

[来自作者的小声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函数

本文完,感谢阅读~