js函数this理解?手写apply、call、bind就够了

1、this是什么?

函数的内部属性,this引用的是函数据以执行的环境对象。也就是说函数的this会指向调用函数的执行环境数组

function a(){
    return  this
}
console.log( a() === window) //true

函数的 this 关键字在 JavaScript 中的表现略有不一样,此外,在严格模式和非严格模式之间也会有一些差异。闭包

function a(){
    'use strict'
    return  this
}
console.log( a() === undefined) //true

2、this指向哪里

this作为函数的关键字,指向函数的调用对象。app

大体的指向总结概括为能够分为如下几种:dom

1.函数作为全局环境调用时

var name = 'window';
function a(){
    return  this.name
}
a() ==='window' //true

2.函数作为对象方法调用时

var obj = {
    name:'obj',
    sayName:function(){
        return this.name
    }
}
console.log( obj.sayName() === 'obj') //true

//稍微改动一下;添加下面代码。

var sayName = obj.sayName;
console.log( sayName() === 'obj') //false

//此时,sayName函数里的this指向了window。

这里须要明白一点,函数名仅仅是一个包含指针的变量,函数是复杂数据类型,因此函数名就只是一个指针,指向堆中的内存地址!因此sayName此时只是复制了指针地址,因此,上面代码改写成下面就很清晰了。

var sayName = function(){
    return this.name
}
var obj = {
    name:'obj',
    sayName:sayName
}
console.log( obj.sayName() === 'obj') //true
console.log( sayName() === 'obj') //false

3.函数作为dom节点事件调用时

var container3 = document.getElementById('container3')
container3.onclick = function(){ //指向节点自己
    console.log(this) //<div id="container3">container3</div>
}

4.作为构造函数实力化方法时

function A(name){
    this.name = name;
    this.sayName = function(){
        console.log(this.name)//指向实例对象
    }
}
var a = new A('aa');
a.sayName(); //aa

5.箭头函数里的this

var name = 'window'
var obj = {
    name:'obj',
    fn:function(){    
       (function (){
        console.log(this.name)
       })()
    }    

}
obj.fn() //window

普通函数,因为闭包函数是window执行的,因此this指向window;
箭头函数的this指向函数建立时的做用域。

var name = 'window'
var obj = {
    name:'obj',
    fn:function(){    
       (()=>{ //改为箭头函数
        console.log(this.name)
       })()
    }    

}
obj.fn()

改为箭头函数,后能够看出建立时的做用域是obj.fn函数执行是的做用域,也就是obj

3、this指向怎么改

js提供了一些能够改变函数执行做用域的方法。由于普通函数若是经过上面的写法来改变this执行时上下文,写法就太过于麻烦。函数

apply、call、bind用法

apply:
fn.apply(thisObj,数组参数)
定义:应用某一个对象的一个方法,用另外一个对象替换当前对象
说明:若是参数不是数组类型的,则会报一个TypeError错误。this

call:
fn.call(thisObj, arg1, arg2, argN)
apply与call的惟一区别就是接收参数的格式不一样。prototype

bind:
fn.bind(thisObj, arg1, arg2, argN)
bind()方法建立一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其他的参数将做为新函数的参数供调用时使用。指针

apply、call、bind实现

apply的实现:

Function.prototype.myApply= function(context){
    context.fn = this;//1.将函数挂载到传入的对象
    var arg = [...arguments].splice(1)[0];//2.取参数
    if(!Array.isArray(arg)) {
        throw new Error('apply的第二个参数必须是数组') //3.限制参数类型为数组
    }    
    context.fn(arg) //4.执行对象的方法
    delete context.fn; //5.移除对象的方法
}

var obj = {
    name:'obj'
}
function sayName(arr){
    console.log(this.name,arr)
}
sayName.myApply(obj,[1,2,3]) //obj [1, 2, 3]
call实现:与apply的惟一区别就是参数格式不一样

Function.prototype.myCall= function(context){
    context.fn = this;//1.将函数挂载到传入的对象
    var arg = [...arguments].splice(1);//2.取参数
    context.fn(...arg) //3.执行对象的方法
    delete context.fn; //4.移除对象的方法
}
var obj = {
    name:'obj1'
}
function sayName(){
    console.log(this.name,...arguments)
}
sayName.myCall(obj,1,2,3,5) //obj1 1,2,3,5
bind在mdn上的实现:

Function.prototype.myBind = function(oThis){
    if(typeof this !== 'function'){
        throw new TypeError('被绑定的对象须要是函数')
    }
    var self = this
    var args = [].slice.call(arguments, 1)
    fBound = function(){ //this instanceof fBound === true时,说明返回的fBound被当作new的构造函数调用
        return self.apply(this instanceof fBound ? this : oThis, args.concat([].slice.call(arguments)))
    }
    var func = function(){}
    //维护原型关系
    if(this.prototype){
        func.prototype = this.prototype
    }
    //使fBound.prototype是func的实例,返回的fBound若做为new的构造函数,新对象的__proto__就是func的实例
    fBound.prototype = new func()
    return fBound
}
相关文章
相关标签/搜索