欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文全面解析JS中的this綁定規(guī)則

 更新時間:2024年04月17日 09:01:01   作者:一溪風月  
寫過JavaScript的都知道,JS中的this相對來講是比較難以捉摸的,尤其在一些復雜的場景下的指向總是讓人摸不著頭腦,所以這篇文章我們就來系統(tǒng)的學習和研究一下this的綁定規(guī)則,需要的朋友可以參考下

大家好,我是一名喜歡摸魚的前端程序員,寫過JavaScript的都知道,JS中的this相對來講是比較難以捉摸的,尤其在一些復雜的場景下的指向總是讓人摸不著頭腦,所以這篇文章我們就來系統(tǒng)的學習和研究一下this的綁定規(guī)則,并且會在文章的最后列出了四道關(guān)于this綁定的題目,有興趣的小伙伴可以自己試著做一下,相信在你耐心看完這些內(nèi)容之后,你會對this有一個更加全面且深刻的理解。

一.直接調(diào)用(獨立函數(shù)調(diào)用/全局調(diào)用)

直接調(diào)用顧名思義就是直接進行函數(shù)的調(diào)用,怎么直接調(diào)用哪? 我們常見的直接調(diào)用分為兩種情況,如下:

function foo () {
  console.log(this)
}
//進行函數(shù)調(diào)用
foo()

這是最常見的一種情況,這種情況this在非嚴格模式的情況下指向的是全局對象,這種調(diào)用方式,我們稱之為獨立函數(shù)調(diào)用,或者稱之為全局調(diào)用。

還有另外一種情況是在對象中定義,然后賦值給一個變量,然后單獨對這個賦值后的函數(shù)進行調(diào)用,依然是獨立函數(shù)調(diào)用或者稱之為全局調(diào)用,非嚴格模式下指向的是window。

let obj = {
  name: "zpj",
  foo: function () {
    console.log(this)
  }
}
let bar = obj.foo
bar()

這種寫法,依然屬于獨立函數(shù)調(diào)用,指向的依然是window或者說全局對象。

注意:獨立函數(shù)調(diào)用(全局調(diào)用)this的指向在嚴格模式下并不指向window而是指向undefined所以我們在使用的時候千萬不要使用this來代替window,而是直接使用window。

二.對象綁定

通過對象綁定比較容易理解,因為我們在平時會經(jīng)常用到,當我們在如下的這種方式進行的時候會指向調(diào)用它的對象obj;

let obj={
  name:"aaa",
  foo:function(){
    console.log(this);
  }
}
obj.foo()

// { name: 'aaa', foo: [Function: foo] }

三.new綁定

在講解new綁定之前,我們先來看下當我們在new一個對象的時候到底做了什么事情。

  • 創(chuàng)建新的空對象。
  • 將this指向這個空對象。
  • 指向函數(shù)體中的代碼。
  • 沒有顯示返回空對象的時候默認返回這個對象。

從第二步我們知道,當我們進行new操作的時候會將this綁定到實例化的這個對象上面。

function foo (name) {
  this.name = name
  console.log(this.name)
}
let bar = new foo("zzz")
console.log(bar)

// zzz
// foo { name: 'zzz' }

通過上述的代碼我們可以看到當我們進行對象實例化的時候函數(shù)內(nèi)部的this指向的是實例化的這個對象。

四.this指向綁定事件的元素

<ul id="color-list">
  <li>red</li>
  <li>yellow</li>
  <li>blue</li>
  <li>green</li>
  <li>black</li>
  <li>white</li>
  </ul>
// this 是綁定事件的元素
// target 是觸發(fā)事件的元素 和 srcElememnt 等價
let colorList = document.getElementById("color-list");
colorList.addEventListener("click", function (event) {
  console.log('this:', this);
  console.log('target:', event.target);
  console.log('srcElement:', event.srcElement);
})

有些時候我們會遇到一些困擾,比如在div節(jié)點的事件函數(shù)內(nèi)部,有一個局部的 callback 方法,該方法被作為普通函數(shù)調(diào)用時,callback 內(nèi)部的this是指向全局對象 window的.

<div id="div1">我是一個div</div>
window.id = 'window';
document.getElementById('div1').onclick = function(){
  console.log(this.id); // div1
  const callback = function(){
    console.log(this.id); // 因為是普通函數(shù)調(diào)用,所以 this 指向 window
  }
  callback();
}

此時有一種簡單的解決方案,可以用一個變量保存 div節(jié)點的引用,如下:

window.id = 'window';
document.getElementById('div1').onclick = function(){
  console.log(this.id); // div1
  const that = this; // 保存當前 this 的指向
  const callback = function(){
    console.log(that.id); // div1
  }
  callback();
}

五.顯式綁定(call/apply)

有的時候this的指向并不能如我們所愿,這個時候我們需要手動去更改this的指向,來滿足我們的需求,其實在JavaScript中給我們提供了能夠更改this的綁定,首先我們看下call和apply。

function foo(name,age){
  console.log(this);
  console.log(name,age);
}
const obj = {
  name:"zs",
  age:12,
}
foo.call(obj,'ls',30)

// { name: 'zs', age: 12 }
// ls 30

通過call函數(shù)我們可以看到我們可以手動的將this綁定到我們新定義的對象上面來,并且通過call單個傳參的方式將參數(shù)傳遞給了這個函數(shù),實現(xiàn)了函數(shù)的調(diào)用和this的綁定。

function foo (name, age) {
  console.log(this)
  console.log(name, age)
}
const obj = {
  name: "zs",
  age: 12,
}
foo.apply(obj, ['nnn', 45])

// { name: 'zs', age: 12 }
// ls 30

我們會發(fā)現(xiàn)我們使用apply的方式進行綁定的修改結(jié)果依然如此,差別在于他們的傳參方式不同,call是單個的方式進行傳參的,而apply是通過數(shù)組的方式傳參的。

六.bind函數(shù)的顯式綁定

bind的綁定和call和apply的使用有些差別,使用bind會生成一個新的函數(shù),這個新函數(shù)我們稱之為BF綁定函數(shù),我們需要手動對這個函數(shù)進行調(diào)用。

function foo(){
  console.log("foo",this)
}

let obj = {
  name:"why"
}

let bar = foo.bind(obj)
bar()

七.內(nèi)置函數(shù)的調(diào)用綁定思考

我們在開發(fā)中會用到很多內(nèi)置的函數(shù),比如定時器setTimeout 這個時候我們需要靠經(jīng)驗來判斷當前的this指向因為有些東西根本不是我們來調(diào)用的,而是函數(shù)內(nèi)部調(diào)用的,我們根本不知道他們做了什么,這些內(nèi)容需要我們自己總結(jié)一下,內(nèi)容如下。

  • 定時器內(nèi)部的函數(shù)this指向window
setTimeout(function () {
  console.log(this, "1")
}, 500)
setTimeout(() => {
  console.log(this, "2")
}, 500)

  • 按鈕的點擊事件,指向事件發(fā)起的對象,也就是綁定事件的元素。
let box = document.querySelector(".test")
box.onclick = function () {
  console.log(this)
}

  • forEach中的this也是指向window
const array = [1, 2, 3, 4, 5, 6]
array.forEach(item => {
  console.log(this)
})

八.綁定優(yōu)先級的比較

我們前面了解的都是一些獨立的規(guī)則,但是實際的情況往往是比較復雜的,可能涉及到多個綁定一起使用的情況,這個時候我們就需要研究一下不同函數(shù)之間調(diào)用的優(yōu)先級。

  • 直接調(diào)用(默認綁定)的優(yōu)先級是最低的。
  • 顯式綁定優(yōu)先級高于對象綁定(隱式綁定)。
function foo () {
  console.log(this.name)
}
let obj = {
  name: "zzz",
  bar: foo
}
obj.bar.call({
  name: "aaa"
})

// aaa
  • new綁定優(yōu)先級高于對象綁定(隱式綁定)的優(yōu)先級
function foo (name) {
  this.name = name
  console.log(this.name)
}
let obj = {
  name: "zzz",
  bar: foo
}
new obj.bar("ccc")

// ccc
  • new綁定不能和call與apply一起使用,new綁定的優(yōu)先級比bind高。
function foo (name) {
  this.name = name
  console.log(this.name)
}

let bar = foo.bind("zzz")
new bar("ccc")

// ccc
  • bind和apply的優(yōu)先級,bind的優(yōu)先級更高,也高于call因為call和apply使用方法一樣。
function foo () {
  console.log(this)
}

let bar = foo.bind("zzz")
bar.call("aaa")

// zzz

九.綁定規(guī)則之外

其實在上述的綁定規(guī)則之外還有許多我們有時候按照規(guī)則難以理解的情況,我們來總結(jié)下有哪些情況。

  • 在顯式綁定當中如果傳入nullundefined這個綁定會被忽略,使用默認規(guī)則,嚴格模式能夠綁定。
function foo () {
  console.log(this)
}

foo.apply(null)
foo.apply(undefined)

// window
// window
  • 創(chuàng)建一個函數(shù)的間接引用,使用函數(shù)的默認綁定規(guī)則,指向window(了解)
var obj1 = {
  name:"obj1",
  foo:function(){
    console.log("foo",this)
  }
}
var obj2={
  name:"obj2"
};
(obj2.foo = obj1.foo)()

// window

十.箭頭函數(shù)的使用

我們之前使用函數(shù)的方式是這樣的。

// 普通函數(shù)方式
function foo1(){}
// 函數(shù)表達式方式
let foo2 = function(){}

箭頭函數(shù)的寫法

// 箭頭函數(shù)的完整寫法
// 1.()函數(shù)的參數(shù)
// 2.{}函數(shù)體
let foo3 = (name,age)=>{
    console.log(name);
    console.log(age);
} 

箭頭函數(shù)與普通函數(shù)的區(qū)別:箭頭函數(shù)中沒有thisarguments并且不能作為構(gòu)造函數(shù)使用。

箭頭函數(shù)的優(yōu)化方式:

  • 當一個參數(shù)的時候可以省略函數(shù)參數(shù)的小括號。
let name = ["abc","bca","nba"];
name.forEach(item=>{
    console.log(item);
})
  • 如果函數(shù)體中只有一行執(zhí)行代碼{}可以省略,但是一行代碼中不能帶return;
let name = ["a","b","c"];
name.filter(item=> console.log(item))
  • 如果函數(shù)體中只有一行代碼,那么這行代碼的返回值會作為整個函數(shù)的返回值。
let name = ["a","b","c"];
name.filter(item=>item==='a')
  • 如果默認返回值是一個對象,那么這個對象必須加小括號
let arr = ()=>({name:"zzz",age:12})

箭頭函數(shù)使用案例:使用所有nums的所有平方和的值。

let nums = [20,30,11,15,111]
/*
*1.首先調(diào)用filter函數(shù)然后返回
*2.然后調(diào)用map函數(shù)然后返回
*3.然后調(diào)用reduce計算后返回。
*/
let nums = [20, 30, 11, 15, 111]
let num = nums.filter(item => item % 2 === 0)
  .map(item => item * item)
  .reduce((pre, cur) => pre + cur, 0)
console.log(num)

// 1300

十一.箭頭函數(shù)中的this

箭頭函數(shù)中是沒有this的,所以this如果在箭頭函數(shù)中的this就是上級作用域的this

let bar =()=>{
  console.log(this)
}
// window

因為沒有this的原因,箭頭函數(shù)中使用顯式綁定也是無法綁定過去的。

let bar =()=>{
  console.log(this)
}
bar.call({name:"zzz"})
// window

我們再來看下一個案例來明白下this的查找規(guī)則。

let obj = {
  name: "obj",
  foo: function () {
    let bar = () => {
      console.log(this)
    }
    return bar
  }
}
let fn = obj.foo()
fn.call("bbb")

// obj
  • 首先調(diào)用foo函數(shù)返回bar定義的函數(shù),箭頭函數(shù)沒有this。
  • 根據(jù)作用域的查找規(guī)則,會向上層找this,foo函數(shù)中是有作用域的。
  • 使用call函數(shù)是無法更改掉this的。
  • 所以打印出來this的指向是obj函數(shù)。

十二.this常見面試題解析

oconst o1 = {
    text: 'o1',
    fn: function () {
        return this.text;
    }
}

const o2 = {
    text: 'o2',
    fn: function () {
        return o1.fn();
    }
}

const o3 = {
    text: 'o3',
    fn: function () {
        var fn = o1.fn;
        return fn();
    }
}

console.log(o1.fn()); // o1
console.log(o2.fn()); // o1
console.log(o3.fn()); // undefined

解析:首先o1.fn是一個對象調(diào)用,所以this指向的是這個對象o1,o2.fn()單調(diào)用的時候返回的是o1.fn因此指向的仍然是o1,第三個o1.fn進行了重新賦值然后調(diào)用,獨立函數(shù)調(diào)用指向undefined。

var name = "window";
var person = {
  name:"person",
  sayName:function(){
    console.log(this.name);
  }
}
function sayName(){
  var sss = person.sayName;
  sss(); // window
  person.sayName(); // person
  (person.sayName)(); // person  等價于 person.sayName()
  (b=person.sayName)(); // window
}
sayName();
var name = 'window'
var person1 = {
  name:'person1'
  foo1:function(){
    console.log(this.name)
  },
  foo2:()=>console.log(this.name),
  foo3:function(){
    return function(){
      console.log(this.name)
    }
  },
  foo4:function(){
    return ()=>{
      console.log(this.name)
    }
  }
}

var person2 = {name:'person2'}
person1.foo1(); // person1
person1.foo1.call(person2); // person2

person1.foo2() // window
person1.foo2.call(person2); // window

person1.foo3()(); //window 獨立函數(shù)調(diào)用
person1.foo3.call(person2)() // window // 默認調(diào)用
person1.foo3().call(person2) // person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
var name = "window"
function Person(name){
  this.name = name
  this.foo1 = function(){
    console.log(this.name)
  }
  this.foo2 = ()=>console.log(this.name)
  this.foo3 = function(){
    return function(){
      console.log(this.name)
    }
  }
  this.foo4 = function(){
    return ()=>{
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1() // person1
person1.foo1.call(person2) //person2
person1.foo2() // person1
person1.foo2.call(person2) // person1
person1.foo3()() // window
person1.foo3.call(perosn2)() // window
person1.foo3().call(person2) // person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
var name = "window"
function Person(name){
  this.name = name
  this.obj = {
    name:'obj',
    foo:function(){
      return function(){
        console.log(this.name)
      }
    },
    foo2:function(){
      return ()=>{
        console.log(this.name)
      }
    }
  }
}

var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()() // window 獨立函數(shù)調(diào)用
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2
person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj

以上就是一文全面解析JS中的this綁定規(guī)則的詳細內(nèi)容,更多關(guān)于JS this綁定規(guī)則的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論