亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频

蟲蟲首頁| 資源下載| 資源專輯| 精品軟件
登錄| 注冊

您現在的位置是:首頁 > 技術閱讀 >  JS基礎精講之【頭疼的this】

JS基礎精講之【頭疼的this】

時間:2024-02-10

前言:一轉眼從事前端已經6年了,從當時的小白到如今大廠的技術專家,中間也走過不少彎路,從今天開始我會持續更新前端技術文章,并且整體的文章會進行體系梳理,整個知識體系分為:基礎精講,框架講解,框架及工具原理,前端面試題精講,大廠面試題收錄精講,前端新技術講解。通過完善的技術精講助力各位快速成長,同時也希望各位能夠在我的幫助下獲得比較好的offer,少走彎路,大家一起加油。那么開始我們的第一篇文章吧。


頭疼的this

    為什么說是頭疼的this,對于常年使用C++,C#,Java等這些面向對象語言的程序員來說,幾乎天天都和this打交道。在這些語言里,this含義非常明確,就是指向當前的對象實例,我們用起來也是相當的放心。然而,到了JavaScript這個動態語言里,this的寫法沒變,但是其含義卻大大地不同了,JavaScript中的this總是讓人迷惑,應該是js眾所周知的坑之一。個人也覺得js中的this不是一個好的設計,由于this晚綁定的特性,它可以是全局對象,當前對象,或者…有人甚至因為坑大而不用this,我們一般在面試中面試官又會扔過來一堆各種各樣的考察this的題目,很是頭疼。

那到底什么是this?this是什么?

    this是指包含它的函數作為方法被調用時所屬的對象。這句話理解起來感覺還是很拗口的,但是如果你把它拆分開來變成這三句話后就好理解一點了。

  • 1.包含它的函數

  • 2.作為方法被調用時

  • 3.所屬的對象

一、this的指向

首先我們先要記住this的幾個特點:

  • this 總是(非嚴格模式下)指向一個對象,而具體指向哪個對象是在運行時基于函數的執行環境動態綁定的,而非函數被聲明時的環境;

  • 除了不常用的with和eval的情況,具體到實際應用中,this指向大概可以分為四種:

    • 作為對象的方法調用;

    • 作為普通函數調用;

    • 構造器調用;

    • call 或 apply調用;

    • 箭頭函數中,this指向函數上層作用域的this;

  • 構造器普通函數的區別在于被調用的方式

  • A,call(B) => 可以理解成在B的作用域內調用了A方法;

1.1 作為對象的方法調用

當函數作為對象的方法被調用時,this指向該對象

var obj = {    a: 'jianxi',    getName: function(){        console.log(this === obj);        console.log(this.a);    }};
obj.getName(); // true jainxi
1.2 作為普通函數被調用

當函數不作為對象的屬性被調用,而是以普通函數的方式,this總是指向全局對象(在瀏覽器中,通常是Window對象)

window.name = 'jianxi';
var getName = function(){ console.log(this.name);};
getName(); // jianxi

或者下面這段迷惑性的代碼

window.name = 'koa'var obj = {    name: 'jianxi',    getName: function(){        console.log(this.name)    }};
var getNew = obj.getName;getNew(); // koa

而在ES5的嚴格模式下,this被規定為不會指向全局對象,而是undefined

1.3 構造器調用

除了一些內置函數,大部分Js中的函數都可以成為構造器,它們與普通函數沒什么不同

構造器普通函數的區別在于被調用的方式:當new運算符調用函數時,總是返回一個對象,this通常也指向這個對象

var MyClass = function(){    this.name = 'jianxi';}var obj = new MyClass()obj.name; // jianxi

但是,如果顯式的返回了一個object對象,那么此次運算結果最終會返回這個對象。

var MyClass = function () {    this.name = 1;    return {        name: 2    }}var myClass = new MyClass();console.log('myClass.name:', myClass.name); // { name: 2}

只要構造器不顯示的返回任何數據,或者返回非對象類型的數據,就不會造成上述問題。

1.4 call或apply調用

跟普通的函數調用相比,用call和apply可以動態的改變函數的this

var obj1 = {    name: 1,    getName: function (num = '') {        return this.name + num;    }};
var obj2 = { name: 2,}// 可以理解成在 obj2的作用域下調用了 obj1.getName()函數console.log(obj1.getName()); // 1console.log(obj1.getName.call(obj2, 2)); // 2 + 2 = 4console.log(obj1.getName.apply(obj2, [2])); // 2 + 2 = 4

1.5箭頭函數

箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。因此,在下面的代碼中,傳遞給setInterval的函數內的this與封閉函數中的this值相同:

this.name = 2var obj = {    name: '1',    getName: () => {        console.log(this.name)    }}
obj.getName()

1.6常見的坑

就像標題一樣,有的時候this會指向undefined

情況一

var obj = {    name: '1',    getName: function (params) {        console.log(this.name)    }};obj.getName();
var getName2 = obj.getName;getName2()

這個時候,getName2()作為普通函數被調用時,this指向全局對象——window。

情況二

當我們希望自己封裝Dom方法,來精簡代碼時:

var getDomById = function (id) {    return document.getElementById(id);};getDomById('div1') //dom節點

那么我們看看這么寫行不行?

var getDomById = document.getElementByIdgetDomById('div1') // Uncaught TypeError: Illegal invocation(非法調用)

這是因為:

  • 當我們去調用document對象的方法時,方法內的this指向document

  • 當我們用getId應用document內的方法,再以普通函數的方式調用,函數內容的this就指向了全局對象。

利用call和apply修正情況二

document.getElementById = (function (func) {    return function(){        return func.call(document, ...arguments)    }})(document.getElementById)// 利用立即執行函數將document保存在作用域中

二、call和apply

不要因為它的“強大”而對它產生抗拒,了解并熟悉它是我們必須要做的,共勉!

1.call和apply區別

先來看區別,是因為它們幾乎沒有區別,下文代碼實例call和apply都可以輕易的切換。

當它們被設計出來時要做到的事情一摸一樣,唯一的區別就在于傳參的格式不一樣

  • apply接受兩個參數

    • 第一個參數指定了函數體內this對象的指向

    • 第二個參數為一個帶下標的參數集合(可以是數組或者類數組)

  • call接受的參數不固定

    • 第一個參數指定了函數體內this對象的指向

    • 第二個參數及以后為函數調用的參數

因為在所有(非箭頭)函數中都可以通過arguments對象在函數中引用函數的參數。此對象包含傳遞給函數的每個參數,它本身就是一個類數組,我們apply在實際使用中更常見一些。

call是包裝在apply上面的語法糖,如果我們明確的知道參數數量,并且希望展示它們,可以使用call。

當使用call或者apply的時候,如果我們傳入的第一個參數為null,函數體內的this會默認指向宿主對象,在瀏覽器中則是window

借用其他對象的方法

我們可以直接傳null來代替任意對象

Math.max.apply(null, [1, 2, 3, 4, 5])

2.call和apply能做什么?

使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數——來時MDN(opens new window)

  • 調用構造函數來實現繼承;

  • 調用函數并且指定上下文的 this;

  • 調用函數并且不指定第一個參數;

1.調用構造函數來實現繼承

通過“借用”的方式來達到繼承的效果:

function Product(name, price) {  this.name = name;  this.price = price;}
function Food(name, price) { Product.call(this, name, price); // this.category = food;}
var hotDog = new Food('hotDog', 20);

2.調用函數并且指定上下文的 this

此時this被指向了obj

function showName() {    console.log(this.id + ':' + this.name);};
var obj = { id: 1, name: 'jianxi'};
showName.call(obj)

3.使用call單純的調用某個函數

Math.max.apply(null, [1,2,3,10,4,5]); // 10

三、模擬實現一個call

先來看一下call幫我們需要做什么?

var foo = {  value: 1};function show() {  console.log(this.value);};show.call(foo); //1

就像解方程,要在已知條件中尋找突破哦口:

  • call 使得this的指向變了,指向了foo;

  • show 函數被執行了;

  • 傳入的參數應為 this + 參數列表;

第一版代碼

上面提到的3點,僅僅完成了一點,且傳入的參數

var foo = {    value: 1}function show() {    console.log(this.value);}
Function.prototype.setCall = function (obj) { console.log(this); // 此時this指向show obj.func = this; // 將函數變成對象的內部屬性 obj.func(obj.value) // 指定函數 delete obj.func // 刪除函數,當做什么都沒發生~}show.setCall(foo)

第二版代碼

為了解決參數的問題,我們要能獲取到參數,并且正確的傳入:

var foo = {    value: 1}function show(a, b) {    console.log(this.value);    console.log(a + b)}Function.prototype.setCall = function (obj) {    obj.fn = this; // 將函數變成對象的內部屬性    var args = [];    for(let i = 1; i < arguments.length; i++){        args.push('arguments[' + i + ']');    }    eval('obj.fn(' + args + ')'); // 傳入參數    delete obj.fn; // 刪除函數,當做什么都沒發生~}
show.setCall(foo, 1, 2); // 1 3

此時,我們就可以做到,傳入多個參數的情況下使用call了,但是如果你僅想用某個方法呢?

第三版代碼

Function.prototype.setCall = function (obj) {  var obj = obj || window;  obj.fn = this;  var args = [];  for(var i = 1, len = arguments.length; i < len; i++) {    args.push('arguments[' + i + ']');  }  var result = eval('obj.fn(' + args +')');  delete obj.fn;  return result;};// 測試一下var value = 2;var obj = { value: 1 };
function bar(name, age) { console.log(this.value); return { value: this.value, name: name, age: age }}bar.setCall(null); // 2console.log(bar.setCall(obj, 'kevin', 18));

三、bind

提到了callapply,就繞不開bind(),來看一下MDN上對**bind()**的解釋:

bind() 方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定為 bind() 的第一個參數,而其余參數將作為新函數的參數,供調用時使用。

bind 可以被new , 可以進行構造函數

我們用Js來模擬一個bind方法,以便加深我們的認識

Function.prototype.newBind = function(context) {    if(typeof this !== 'function') {        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');    }    var self = this;    var args = Array.prototype.slice.call(arguments, 1);  // 間接調用數組方法,獲取第一次傳的參數
let tempFn = function {}; // 利用一個空函數作為中轉
tempFn.prototype = this.prototype; // 修改返回函數的 prototype 為綁定函數的 prototype,實例就可以繼承綁定函數的原型中的值
var resultFn = function () { var innerArgs = Array.prototype.slice.call(arguments); if (this instanceof tempFn) { // 如果 返回函數被當做構造函數后,生成的對象是 tempFn 的實例,此時應該將 this 的指向指向創建的實例。 return self.apply(this, args.concat(innerArgs)); } else { return self.apply(context, args.concat(innerArgs)) } }
resultFn = new tempFn(); return resultFn;}


這樣看上去,bind總會幫我們返回同樣的this值,還是挺堅挺的哦~


如果您看到了最后,不妨收藏、點贊、評論一下吧!!!

持續更新,您的三連就是我最大的動力,共勉!





亚洲欧美第一页_禁久久精品乱码_粉嫩av一区二区三区免费野_久草精品视频
亚洲国产一成人久久精品| 久久精品夜色噜噜亚洲aⅴ| 亚洲一本大道在线| 欧美特黄一级| 亚洲在线观看免费视频| 国产精品福利影院| 久久国产精品久久国产精品| 怡红院精品视频| 欧美视频亚洲视频| 欧美一区二区三区视频在线观看| 国产精品mv在线观看| 久久久国际精品| 中文国产一区| 国模套图日韩精品一区二区| 欧美激情精品久久久久久大尺度| 中文亚洲字幕| 禁断一区二区三区在线| 国产精品亚洲一区二区三区在线| 久久一区二区三区四区五区| 亚洲一区二区三区在线观看视频 | 黄色成人片子| 欧美成人嫩草网站| 午夜久久资源| 日韩午夜av| 怡红院av一区二区三区| 午夜一区不卡| 免费短视频成人日韩| 一区二区在线免费观看| 欧美成人按摩| 亚洲男人的天堂在线| 伊人蜜桃色噜噜激情综合| 欧美日韩免费看| 久久频这里精品99香蕉| 亚洲欧美日本伦理| 亚洲精品永久免费| 伊大人香蕉综合8在线视| 欧美精品v国产精品v日韩精品| 欧美日韩在线免费视频| 亚洲一区二区黄| 亚洲第一区在线观看| 国产欧美日韩综合| 欧美日韩妖精视频| 欧美日韩成人在线视频| 欧美成人伊人久久综合网| 久久久久国产精品厨房| 性欧美大战久久久久久久久| 亚洲天堂免费在线观看视频| 亚洲精品裸体| 亚洲第一综合天堂另类专| 国产亚洲一级高清| 国产欧美va欧美va香蕉在| 国产精品久久久久久久久久久久久久 | 国产日韩欧美在线观看| 欧美日韩国产限制| 欧美人与性禽动交情品| 欧美日韩精品免费观看视频完整| 欧美激情按摩在线| 欧美日韩在线一区二区三区| 欧美天堂亚洲电影院在线播放 | 亚洲精品社区| 一区二区亚洲精品国产| 国语自产精品视频在线看抢先版结局 | 在线播放国产一区中文字幕剧情欧美| 国产视频观看一区| 夜夜嗨网站十八久久| 亚洲国产乱码最新视频| 伊人久久婷婷| 亚洲精品免费看| 日韩视频永久免费| 亚洲香蕉视频| 久久久久久久综合狠狠综合| 久久午夜精品| 欧美日韩免费一区二区三区视频 | 亚洲精选一区| 亚洲视频在线播放| 久久久久久夜| 欧美日韩国产不卡| 国产精品成人一区二区三区吃奶 | 亚洲高清视频一区二区| 亚洲日本免费电影| 99热精品在线观看| 亚洲专区免费| 欧美在线二区| 欧美精品 日韩| 国产精品视频专区| 激情文学综合丁香| a4yy欧美一区二区三区| 亚洲人成小说网站色在线| 亚洲午夜伦理| 久久久人成影片一区二区三区| 欧美成人免费播放| 国产毛片精品国产一区二区三区| 永久域名在线精品| 亚洲欧美视频一区| 欧美国产精品v| 国产精品无人区| 亚洲全黄一级网站| 欧美中文字幕| 国产精品观看| 亚洲久久一区| 免费成人av资源网| 国产亚洲成av人片在线观看桃| 日韩午夜剧场| 欧美www在线| 曰韩精品一区二区| 欧美亚洲网站| 欧美视频一区二区在线观看| 亚洲黄色天堂| 久久综合网络一区二区| 国产欧美在线视频| 亚洲欧美日韩综合国产aⅴ| 欧美激情一区二区三区高清视频 | 亚洲美女色禁图| 蜜臀a∨国产成人精品| 国产一区二区三区的电影| 亚洲一区三区电影在线观看| 欧美日本精品| 亚洲精品一区中文| 欧美黄网免费在线观看| 一区二区三区在线免费视频| 久久精品1区| 狠狠色狠狠色综合| 欧美一区二区三区电影在线观看| 国产精品igao视频网网址不卡日韩| 91久久久国产精品| 欧美成人精品激情在线观看| 亚洲大胆女人| 欧美黑人在线观看| 亚洲另类视频| 欧美色道久久88综合亚洲精品| 在线视频亚洲一区| 国产精品久久久久久久浪潮网站| 亚洲一区二区三区高清 | 亚洲美女在线国产| 美女精品视频一区| 亚洲欧洲一区二区在线观看| 欧美激情中文字幕一区二区| 亚洲人成小说网站色在线| 模特精品在线| 一区二区三区 在线观看视| 国产精品成人va在线观看| 午夜国产一区| 在线观看亚洲a| 欧美精品一区二区三区视频| 99精品视频免费观看| 国产精品久久777777毛茸茸| 久久精品国产亚洲一区二区三区| 国产一区二区三区免费不卡| 麻豆国产精品一区二区三区| 一本在线高清不卡dvd| 国产精品视频午夜| 久久综合伊人| 亚洲精品专区| 国产免费观看久久| 免费观看日韩| 亚洲欧美日韩精品在线| 一区在线影院| 国产精品v日韩精品| 久久久久五月天| 一本一本久久a久久精品综合麻豆 一本一本久久a久久精品牛牛影视 | 欧美影院成年免费版| 伊人成人在线视频| 欧美无乱码久久久免费午夜一区| 欧美一区二区三区婷婷月色 | 国产免费观看久久黄| 噜噜噜噜噜久久久久久91| 91久久在线播放| 免费在线观看日韩欧美| 亚洲黄色有码视频| 欧美激情亚洲视频| 亚洲女性裸体视频| 精品福利免费观看| 欧美性猛交xxxx乱大交蜜桃 | 日韩一级大片在线| 国产欧美日韩一区二区三区在线观看 | 久久av红桃一区二区小说| 伊人婷婷欧美激情| 国产精品人人爽人人做我的可爱| 久久久久这里只有精品| 亚洲一区二区免费视频| 亚洲国产精品国自产拍av秋霞| 国产精品网站在线观看| 欧美三级网址| 欧美另类综合| 欧美成人精精品一区二区频| 欧美在线观看视频在线| 一本色道88久久加勒比精品| 一区在线免费观看| 国产欧美视频在线观看| 欧美日韩国产专区| 免费看亚洲片| 久久综合狠狠综合久久激情| 欧美专区18| 性欧美18~19sex高清播放| 亚洲视频免费在线| 亚洲日本中文字幕| 亚洲激情第一页| 亚洲电影免费观看高清完整版| 红桃av永久久久| 国语自产精品视频在线看|