iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Modern Web

初學者跪著學JavaScript系列 第 25

初學者跪著學JavaScript Day25 : 寧願找this也不碰歷史

一日客語:中文:太陽 客語:ngidˋ teuˇ(日頭)

在學建構函式時很常看到this,建構函式內的this會代表新創健的實例(物件),this到底是什麼呢?

函式呼叫傳給函式有哪些?

    1. 引數傳到參數
    1. this(隱含式參數)

this又稱為函式背景空間(function context),就是函式所在的背景空間

那就會跟函式如何被呼叫有關係

  • 一般函式呼叫:包含函式宣告、函式運算式、立即函式是使用()呼叫,this指向全域
  • 物件的方法呼叫:this指向物件
  • 建構函式呼叫
  • apply、call 呼叫
  • 箭頭函式沒有自己的this,但在定義時記住this值

會隨著呼叫函式不同,他們的this會些許不同


一般函式呼叫:

this會是windows物件,那在嚴格模式下會是undefined

function pig(){
return this}

function dog(){
"use strict"
return this}

pig()//Window
dog()//undefined

// 當使用立即函式
(function pig(){
return this})() //Window

看看瀏覽器顯示畫面:
一般函式:
普通模式(pig())和嚴格模式(dog())

立即函式:


作為物件methods來呼叫:

animal物件裡有一個myPig的property name

他的property vlaue 存放一個函式的reference,記得不用括號

像是這樣[Function: pig] 是函數物件

呼叫這函式來看看返回this是什麼吧。

this是myPig

function pig() {
    return this;
}

const animal = {
    myPig: pig,
};

console.log(animal.myPig); //[Function: pig]

//pig function回傳是this,this是不是animal物件
console.log(animal.myPig() === animal); //true

使用另一個物件crazyanimal來呼叫pig()
此時這個this 會是crazyanimal

const crazyanimal = {
    myPig: pig,
};

console.log(crazyanimal.myPig() === crazyanimal);//true

結論:在不同物件內存pig function 的reference

某物件.pig()呼叫函式 ,this會是指某物件


作為建構器來呼叫

新物件會被當作this

會像是

let 新物件1={
    say:function () {
        return this; //新物件1
}
let 新物件2={
    say:function () {
        return this;//新物件2
}

function Pig() {
    this.say = function () {
        return this;
    };
}
let pigA = new Pig();
let pigB = new Pig();
console.log(pigA.say()); //Pig { say: [Function (anonymous)] }
console.log(pigA.say() === pigA);
console.log(pigB.say() === pigB);

以為這樣就結束了嗎???

當建構函式有回傳值,那該怎麼辦


討論:new+建構函式

建構函式會以大寫開頭

1.當回傳值:Primitive values (基本型別)
2.當回傳值:物件

當回傳值:Primitive values (基本型別)

會回傳新物件

印出會是Pig { say: [Function (anonymous)]

function Pig() {
    this.say = () => '喉喉喉喉';
    return 1000;
}

let littlePig = new Pig();
console.log(littlePig);//Pig { say: [Function (anonymous)] }

當回傳值:物件

會回傳return 的物件
印出會是 { }

function Pig() {
    this.say = () => '喉喉喉喉';
    return {};
}

let littlePig = new Pig();
console.log(littlePig);//{}
const myobj = {
    cat1: 'nini',
    cat2: 'niki',
};

function Pig() {
    this.say = () => '喉喉喉喉';
    return myobj;
}

let littlePig = new Pig();
console.log(littlePig);//{ cat1: 'nini', cat2: 'niki' }

Note:
建構器的回傳值是某一個物件時,那麽新物件會被丟棄而回傳那個物件

那如果回傳不是物件則會回傳新物件


筆記:哪一個物件會成為函式背景空間(this)

呼叫
一般函式:this會是windows
建構函式:this會是實例物件
物件方法:this會是方法的物件

但我想要自行設定this呢 ???你可以使用apply、call

使用apply 、 call 方法

建立函式 >> 建構函式

所有函式(object)都具備applycall方法
因為一般函式上一層是建構函式Function他的prototype有apply()和call()方法

apply(當作函式背景空間的物件,用來當作呼叫引數的陣列值)
call(當作函式背景空間的物件,用來當作呼叫引數,但不是陣列值)


是呼叫函式的方法

function Pig() {
    console.log('哈囉');
}
Pig.call();//哈囉
Pig.apply();//哈囉

和Pig()一樣都是會呼叫函式

使用call、apply時
此時this會是第一個參數的物件

function Pig() {
    console.log(this);
}
Pig();//this會是windows

let obj = {};
Pig.call(obj);//{}  指定的{}
Pig.apply(obj);//{} 指定的{}

可以知道使用call和apply呼叫與一般函式呼叫差異

function person() {
    console.log(this.money);
}

let wendy = { money: 1000000000 };
let ann = { money: 300000000 };

person.call(wendy);//1000000000 ,使用這個this會是wendy
person.call(ann);//300000000,使用這個this會是Ann

apply

函數物件.apply(當作函式背景空間的物件,用來當作呼叫引數的陣列值)

Pig.apply(animal,[10,9,8,7]

call

Function.prototype.call

函數物件.call(當作函式背景空間的物件,用來當作呼叫引數,但不是陣列值)

Pig.call(animal,10,9,8,7)


如下:

let wendy = {
    money: 1000000000,
    sleep: function () {
        console.log(this.money);
    },
};
let ann = { money: 300000000 };

console.log(wendy.sleep()); //印出1000000000
//想指定this為ann時
console.log(wendy.sleep.apply(ann)); //印出300000000


當function 有參數時

function person(value1, value2) {
    console.log('value1:', value1);
    console.log('value2:', value2);
    console.log(this.money);
}

let wendy = {
    money: 1000000000,
};
let ann = { money: 300000000 };
person.call(wendy);

//結果
//value1: undefined
//value2: undefined
//1000000000

可以使用第二個參數為引數

apply是要使用陣列

function person(age, score) {
    console.log('age:', age);
    console.log('score:', score);
    console.log(this.money);
}

let wendy = {
    money: 1000000000,
};
let ann = { money: 300000000 };
person.call(wendy, 10, 1000000);


//結果
//age: 10
//score: 1000000
//1000000000

person.apply(wendy, [10, 1000000]);


//結果
//age: 10
//score: 1000000
//1000000000

箭頭函式Arrow Functions

mdn:

箭頭函式並不擁有自己的 this 變數;使用的 this 值來自封閉的文本上下文,也就是說,箭頭函式遵循常規變量查找規則。因此,如果在當前範圍中搜索不到 this 變量時,他們最終會尋找其封閉範圍。

this會到(lexical scoping)scope找this,不是以呼叫方式來看

Arrow functions do not bind their own this, instead, they inherit the one from the parent scope, which is called "lexical scoping".

不是使用箭頭函式時

如下:

此時this是animal

let animal = {
    myfunction: function () {
        return this;
    },
};

console.log(animal.myfunction() === animal); //true

箭頭函式時,this是window

let animal = {
    //此時this是window)
    myfunction: () => this,
};

console.log(animal.myfunction() === window); //true

今日結束~

Day 21:箭頭函數 (Arrow Functions) 的 this 和你想的不一樣 (1)
Understanding "this" in javascript with arrow functions
忍者開發技巧探秘第二版


上一篇
初學者跪著學JavaScript Day24 : 原型不會,但你還有class
下一篇
初學者跪著學JavaScript Day26 : 認識生成器,chris不生氣
系列文
初學者跪著學JavaScript30

尚未有邦友留言

立即登入留言