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
}
相關文章
相關標籤/搜索