1. 程式人生 > >javascript中的一般函式與ES6的箭頭函式對比,以及this指向問題的深度理解

javascript中的一般函式與ES6的箭頭函式對比,以及this指向問題的深度理解

關於this的指向問題,老外有文章寫得非常棒,在看了老外的文章
並結合自己的想法再修改此篇文章

一 、基本概念

  • 本文定義的一般函式 單純 指的是js原生函式(ES5函式)
  • 同時ES5中的作用域只有全域性作用域(window)以及 函式體作用域
  • (也就是說{}本身在ES5中沒有作用域的概念)
  • 本文箭頭函式 是建立在ES6環境中的特殊函式 ,因為箭頭函式空有一生功夫(可呼叫,可執行),卻缺乏內涵(使用空間),這個概念本來就很奇特

二、等價公式

1.js最簡單的一般函式呼叫

   function a(i){
       return i
   }
   console.log(a(1)) // ======>   1

2.ES6箭頭函式呼叫,

	let a = i => i;
	console.log(a(2))
值得注意的是以上兩者在包裹了{}的函式體中,沒有return 就相當於 return undefined
let a3 = i => {i};
console.log(a3(1)); // undefined 
而 
let a4 = i=>i; 預設是返回i的
console.log(a4(1)); // 1

函式keli化

1.js中的一般函式 ---- 柯里函式

  • 其實質就是一單個引數作為函式的輸入,最後輸出一個函式
	function do1(a){
	    console.log("a",a) //1.a 6
	    return function(b){
	        console.log("b",b)//2.b 2
	        return a+b
	    }
	}
	console.log(do1(6)(2))//8

2.ES6中箭頭函式等價函式

	let do2 = a=>b=>a+b;
	console.log(do2(3)(7)) // 10

2.1 keli進階

1. js一般化 柯里變形

	function do2(a){
	    return function d3(param){
	        return function d4(param){
	            return param(a)
	        }
	    }
	}

2. ES6 keli等價,

其實質是一般函式中的d3 d4這種函式名沒有多大意義
	let do33 = a=>param=>param=>param(a);

三、this 在一般函式與箭頭函式之間的終極理解

  • function 中(一般函式(es5函式))的this,是指向呼叫該function的最近一層的執行時物件;箭頭函式 內部是不存在this的(官方說法就是箭頭函式本身並沒有一般函式的作用域的概念,說白一些,空有一身功夫,可以打拳,但是缺乏內涵),所以在箭頭函式中的this本就不屬於箭頭函式中的
  • this 查詢值的次序
  • 先找this指向的名稱空間(作用域),然後找到名稱空間(作用域)中的屬性,
  • {}字典(在js中叫做例項,例項化過的)沒有名稱空間,
    ** 1.this 指向 的是(.)前面的物件,this不是變數而是物件,只有一般函式裡面有this,且this指向的是包裹一般函式的物件
    ** 2.名稱空間(函式作用域)的作用是變數訪問用的
    ** 3.箭頭函式不繫結this
    ** 4.https://www.cnblogs.com/pssp/p/5216085.html
### 例1
let a =1
let obj = {
    a:2,
    b:function(){
        console.log(this)//
    }
}
obj.b()//{a:2,b:}  
// 一般函式中的this指的是obj (.前面的物件的),所以,誰呼叫b,誰就是this
let bb = obj.b
bb() 
// window bb() 相當於 在模組內部新增一個 var bb = bb(),而bb此時是在window下的
所以
let a =1
let obj = {
    a:2,
    b:function(){
        console.log(this.a)
    }
}
obj.b()//2
let bb = obj.b
bb() // 1 
### this的生產應用
	其實各種監聽事件函式(callback)window.addEvent裡面寫的回撥函式裡面指定的this
	就是指向window,這也是內部函式中的this為什麼會指向window的原因,因為
	一旦函式的裡面再巢狀一層函式,層層相扣,最後裡面的this滿天飛,那麼為了最簡
	單明瞭起見,this都會指向window,這也符合外國人比較直接的思維方式,不像中
	國人人那樣的複雜性思維!!
let a =1
let obj = {
    a:2,
    b:function(){
        return function(){console.log(this)}//
    }
}
obj.b()()
//Window 是因為obj.b() 相當於輸出的是一個函式,類似於上一個例子的
//var bb =obj.b();bb(),而這個函式是繫結到window下的

###  例2.[私有函式](https://blog.csdn.net/JustOneRoad/article/details/7306762)
 [私有變數地址](https://www.cnblogs.com/tianxintian22/archive/2015/12/14/5045017.html)
let obj = {
    a:2,
    b:function(){
        var handle = function(){ //在函式內部定義的變數(包括函式)
            console.log(this)
        }
        handle()
    }
}
obj.b()//window
// 可以發現this指向的是window,相當於函式內部執行了window.handle()
//但是 window中沒有handle變數,這是一次性的,在函式內部定義的變數均是私有變數
//js預設會執行
**閉包**

var f = function(){
(function(){console.log(this)})()
}
f() // window
// 私有變數類似於閉包

#### 箭頭函式 例1
let a =1
let obj = {
    a:2,
    b:()=>{
        console.log(this)
        //箭頭函式的this尋找方法是程式碼文字級別(非記憶體空間)的向上查詢,
        //不管物件是誰,但是物件沒有this屬性,不管上下文
        // 可以開啟瀏覽器 F12 輸入 this看看會出現什麼
	}
}
obj.b()//Window 指的是最上一層的window this

#### 箭頭函式 例2

let a =1
let obj = {
a:2,
b:function(){
return ()=>{
console.log(this) // 在this的上一層是函式,而函式本身是有this屬性的
}
}
}
obj.b()()//{a:2,b:f}


### 四、 改變this屬性

/**

  • 改變this的三大方法(三種方法都可以傳一個物件引數,作為this的物件)
  • bind apply call
  • 這三種方法最本質的區別是
  • 1.bind只是繫結this傳入函式體中,並不執行函式
  • 2.apply和call在傳入函式體中就會預設執行函式
    */
#### 1.bind 並不執行函式的直觀試驗
let a =2;
let bindFunc = {
    a:1,
    b:function(){
        return (function(){
            console.log(this)//這裡的this指的是bindFunc看下方的.之前的物件,如果不bind就直接指向window,因為這是內部函式形式
        }).bind(this)
    }

}
bindFunc.b()//返回函式function () { … }
bindFunc.b()()//{a: 1, b: }
#### 2.apply和call函式的直觀試驗

let a =2;
let callFunc = {
a:1,
b:function(){
return (function(){
console.log(this)//這裡的this指的是bindFunc看下方的.之前的物件,如果不bind就直接指向window,因為這是內部函式形式
}).call(this)
}
}
callFunc.b()//{a: 1, b: }

let a =2;
let applyFunc = {
    a:1,
    b:function(){
        return (function(){
            console.log(this)//這裡的this指的是bindFunc看下方的.之前的物件,如果不bind就直接指向window,因為這是內部函式形式
        }).apply(this)
    }
}
applyFunc.b()//{a: 1, b: }


 #### 3.apply與 call傳入其他物件,比如window的話

let a =2;
let callFuncW = {
    a:1,
    b:function(){
        return (function(){
            console.log(this)//這裡的this指的是bindFunc看下方的.之前的物件,如果不bind就直接指向window,因為這是內部函式形式
        }).call(window)
    }
}
callFuncW.b()//window
let a =2;
let applyFuncW = {
    a:1,
    b:function(){
        return (function(){
            console.log(this)//這裡的this指的是bindFunc看下方的.之前的物件,如果不bind就直接指向window,因為這是內部函式形式
        }).apply(window)
    }
}
applyFuncW.b()//window

### 五、高階訓練

#### 訓練一
var a = 100;
var b = {
    a:1000,
    b:{
        a:1,
        c:{
            getA:function(){
                console.log(this) //{ getA: [Function: getA] }
                console.log(this.a) // undefined
            }
        }
    }
}
b.b.c.getA()
#### 訓練二
var a = 100;
var b = {
    a:1000,
    d:()=>{
        console.log(this.a)
    },
    b:{
        a:1,
        d:()=>{
            console.log(this.a)
        },
        c:{
            a:2,
            getA:()=>{
                console.log(this) // window
                console.log(this.a) //  如果 var宣告的,並且沒有閉包,就直接執行到 a
            }
        }
    }
}

b.b.c.getA() 
b.d()
b.b.d()


#### 訓練4

var b = {
a:1000,
d:()=>{
console.log(this.a)
},
b:function(){
var data={
a:1,
d:()=>{
console.log(this.a)
},
c:{
a:2,
getA:()=>{
console.log(this)//{a: 1000, d: ƒ, b: ƒ}
console.log(this.a)//1000
}
}
}
return data
}
}
b.b().c.getA() //


大家有沒有作對?