What's this?

What's this?

由于運行期綁定的特性俊抵,JavaScript 中的 this 含義非常多,它可以是全局對象谜诫、當前對象或者任意對象牵祟,這完全取決于函數(shù)的調(diào)用方式

隨著函數(shù)使用場合的不同,this的值會發(fā)生變化弱卡。但是有一個總的原則乃正,那就是this指的是,調(diào)用函數(shù)的那個對象

作為函數(shù)調(diào)用

在函數(shù)被直接調(diào)用時this綁定到全局對象婶博。在瀏覽器中瓮具,window 就是該全局對象

<script>
        var b =2
        console.log(this)//在全局作用域下,this代表window
        function fn1(){
         var b=1
          console.log(this);//window
          console.log(this.b) //2
         }
fn1();
</script>
<script>
function fn1(){
         var b=1
          console.log(this.b) //undefined
         }

fn1()
<script>
<script>
        var b =2
        function fn1(){
        b=1
          console.log(this);//window
          console.log(this.b) //1
         }
fn1();
</script>

在全局作用域下聲明的變量凡人,

var a =1 //  相當于window.a = 1 也等于this.a = 1

內(nèi)部函數(shù)

函數(shù)嵌套產(chǎn)生的內(nèi)部函數(shù)的this不是其父函數(shù)名党,仍然是全局變量

function fn0(){
    function fn(){
        console.log(this); //window
    }
    fn();
}

fn0();

setTimeout、setInterval

這兩個方法執(zhí)行的函數(shù)this也是全局對象

document.addEventListener('click', function(e){
    console.log(this);//document
    setTimeout(function(){
        console.log(this); //window
    }, 200);
}, false);

如果 想要setTimeout里面的this代表document

document.addEventListener('click', function(e){
    console.log(this);//document
    var _this = this
    setTimeout(function(){
        console.log(_this); //document
    }, 200);
}, false);

作為構造函數(shù)調(diào)用

所謂構造函數(shù)挠轴,就是通過這個函數(shù)生成一個新對象(object)传睹。這時,this就指這個新對象

new 運算符接受一個函數(shù) F 及其參數(shù):new F(arguments...)岸晦。這一過程分為三步:

1.創(chuàng)建類的實例欧啤。這步是把一個空的對象的__proto__屬性設置為 F.prototype 。
2.初始化實例启上。函數(shù) F 被傳入?yún)?shù)并調(diào)用邢隧,關鍵字 this 被設定為該實例。
3.返回實例冈在。

function Person(name){
    this.name = name;
}
Person.prototype.printName = function(){
    console.log(this.name);
};

var p1 = new Person('Byron');
var p2 = new Person('Casper');

p1.printName();//Byron
p2.printName();//Casper

作為對象方法調(diào)用

在 JavaScript 中倒慧,函數(shù)也是對象,因此函數(shù)可以作為一個對象的屬性,此時該函數(shù)被稱為該對象的方法纫谅,在使用這種調(diào)用方式時炫贤,this 被自然綁定到該對象

var obj1 = {
    name: 'Byron',
    fn : function(){
        console.log(this); //obj1
    }
};

obj1.fn();//誰調(diào)用,就指向誰(obj1)
var obj3 = {
    name:'lucas',
    obj1: {
    name: 'Brown',
    fn : function(){
        console.log(this); //obj1
    }
  }
}

obj3.obj1.fn(); //誰調(diào)用系宜,就指向誰(obj3.obj1)

小陷阱

var fn2 = obj1.fn;
fn2(); //window
//函數(shù)調(diào)用只有一種形式func.call(context, p1, p2)
// 由于沒有傳 context
// 所以 this 就是 undefined
// 最后瀏覽器給你一個默認的 this —— window 對象

改變this指向的三個方法: bind照激,call,apply

不采用這三個方法可能會遇到這樣的問題盹牧,采用這種方法調(diào)用this.user相當于window.usr俩垃,所以返回undefined。

var a = {
    user:"追夢子",
    fn:function(){
        console.log(this.user);
    }
}
var b = a.fn;
b(); //undefined

我們直接執(zhí)行a.fn()是可以的

var a = {
    user:"追夢子",
    fn:function(){
        console.log(this.user);
    }
}
a.fn(); //追夢子

雖然這種方法可以達到我們的目的汰寓,但是有時候我們不得不將這個對象保存到另外的一個變量中口柳,那么就可以通過以下方法。

使用call和apply設置this

call有滑,apply跃闹,調(diào)用一個函數(shù),傳入函數(shù)執(zhí)行上下文(context級this)及參數(shù)

fn.call(context, param1, param2...)
fn.apply(context, paramArray)

通過call或者apply方法毛好,第一個參數(shù)都是設置希望this所指向的那個對象望艺,即把fn添加期望運行的context環(huán)境中。
不同之處在于call方法接收參數(shù)列表肌访,而apply接收參數(shù)數(shù)組找默。

var a = {
    user:"追夢子",
    fn:function(){
        console.log(this.user);
    }
}
var b = a.fn
b.call(a); /*也可以是 b.apply(a);*/

注意:如果call和apply的第一個參數(shù)寫的是null,那么this指向的是window對象

使用bind設置this

bind方法和call吼驶、apply方法有些不同惩激,但它們都可以用來改變this的指向。

Function.prototype.bind(context, param1, param2...)

任何函數(shù)都有bind這樣一個方法蟹演。bind风钻,返回一個新的函數(shù),函數(shù)內(nèi)部的this為你傳入的第一個參數(shù)酒请。
仿照之前call和apply的寫法骡技,我們使用bind

var a = {
    user:"追夢子",
    fn:function(){
        console.log(this.user); //追夢子
    }
}
var b = a.fn;
b.bind(a);

發(fā)現(xiàn)代碼沒有被打印,對羞反,這就是bind和call哮兰、apply方法的不同,實際上bind方法返回的是一個修改過后的函數(shù)苟弛。執(zhí)行該函數(shù)看看。

var a = {
    user:"追夢子",
    fn:function(){
        console.log(this.user); //追夢子
    }
}
var b = a.fn;
b.bind(a)(); // "追夢子"

同樣bind也可以有多個參數(shù)阁将,并且參數(shù)可以執(zhí)行的時候再次添加

var a = {
    user:"追夢子",
    fn:function(e,d,f){
        console.log(this.user); 
        console.log(e,d,f); 
    }
}
var b = a.fn;
var c = b.bind(a,10,44);
c(33);

更多例子

bind的例子

var obj3={
        name: 'apple'
    }
var obj1={
    name: 'Brown',
    fn : function(){
        console.log(this);
    }
}

var fn3 = obj1.fn.bind(obj3)
fn3() 
//[object Object] {
//  name: "apple"
//}
//此時的this就是你傳入的第一個參數(shù)obj3
想要setTimeout里面的this代表document的另一種實現(xiàn)方式
#通過bind綁定的this是setTimeout函數(shù)外部的this膏秫,即document
document.addEventListener('click', function(e){
    console.log(this);//document
    setTimeout(function(){
        console.log(this); //document
    }.bind(this), 200);
}, false);

call和apply設置this的例子

var value =100
var obj4 = {
    value:200
}
function fn4(a,b){
    console.log(this.value+a+b)
}
fn4(3,4) //107
fn4.call(obj4,3,4) //207
fn4.apply(obj4,[3,4]) //207

應用

var arr = [1,2,7,4]
//Math.max(1,2,7,4)
console.log(Math.max.apply(null,arr)) //7
function joinStr(){
    //console.log(Array.prototype.join.call(arguments,'-'))
    var join = Array.prototype.join.bind(arguments)
    console.log(join('-'))
}
joinStr('a','b','c') //a-b-c

arguments

1.在函數(shù)調(diào)用時,會在函數(shù)內(nèi)部自動生成一個名為 arguments的隱藏對象
2.為類數(shù)組對象窘哈,可以使用[]運算符獲取函數(shù)調(diào)用時傳遞的實參
3.只有函數(shù)被調(diào)用時亭敢,arguments對象才會創(chuàng)建,未調(diào)用時其值為null

unction fn5(name, age){
     console.log(arguments);// ["Byron", 20]
     name = 'XXX';
     console.log(arguments); //["XXX", 20]
     arguments[1] = 30;
     console.log(arguments); //["XXX", 30]
 }
 fn5('Byron', 20);

函數(shù)的三種變量

  • 實例變量:(this)類的實例才能訪問到的變量
  • 靜態(tài)變量:(屬性)直接類型對象能訪問到的變量
  • 私有變量:(局部變量)當前作用域內(nèi)有效的變量
function ClassA(){
    var a = 1; //私有變量帅刀,只有函數(shù)內(nèi)部可以訪問
    this.b = 2; //實例變量,只有實例可以訪問
}

ClassA.c = 3; // 靜態(tài)變量骇窍,也就是屬性锥余,類型訪問

console.log(a); // error
console.log(ClassA.b) // undefined
console.log(ClassA.c) //3

var classa = new ClassA();
console.log(classa.a);//undefined
console.log(classa.b);// 2
console.log(classa.c);//undefined

原型與原型鏈

回顧一下類、實例嘲恍、prototype雄驹、__proto__的關系


1.我們通過函數(shù)定義了類Person,類(函數(shù))自動獲得屬性prototype
2.每個類的實例都會有一個內(nèi)部屬性__proto__吁脱,指向類的prototype屬性

因為prototype本質上是個類Object的實例彬向,所以prototype也和其它實例一樣也有個__proto__內(nèi)部屬性,指向其類型Object的prototype

類型

instanceof操作符娃胆,判斷一個對象是不是某個類型的實例

function Person(nick, age){
    this.nick = nick;
    this.age = age;
}
Person.prototype.sayName = function(){
    console.log(this.nick);
}

var p1 = new Person();

判斷p1是不是Person類型的實例,

p1 instanceof Person //true
//其中經(jīng)歷了一下過程
//先查找p1._proto_ === Person.prototype
//如果上面查找未成功凿蒜,再查找p1._proto_._proto_ === Preson.prototype
原型鏈

繼承

繼承是指一個對象直接使用另一對象的屬性和方法胁黑。
JavaScript并不提供原生的繼承機制废封,但可以自己實現(xiàn)

只要實現(xiàn)了兩點的話就可以說我們實現(xiàn)了繼承
1.得到一個類的屬性
2.得到一個類的方法

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.sayName = function(){
    console.log('My name is'+this.name);
};
Person.prototype.walk = function(){
    console.log(this.name+'is walking')
}
function Student(name, age,sex){
}
Student.prototype.doing = function(){
    console.log("I am studying");
};

var s = new Student('hunger',2,'boy')
#問題:怎么讓Studen繼承Person的屬性name, age和方法sayName呢丧蘸?

屬性獲取

對象屬性的獲取是通過構造函數(shù)的執(zhí)行,我們在一個類中執(zhí)行另外一個類的構造函數(shù)刽漂,就可以把屬性賦值到自己內(nèi)部演训,但是我們需要把環(huán)境改到自己的作用域內(nèi)贝咙,可以借助函數(shù)call或bind

function Student(name, age,sex){
    Person.call(this,name,age)
    //Person.bind(this,name,age)
    this.sex = sex
}

方法獲取

Object.create(proto)
創(chuàng)建一個新的對象,新對象的原型就是傳入的參數(shù)

類的方法都定義在了prototype里面庭猩,所以只要我們把子類的prototype改為父類的prototype的備份就好了

Student.prototype = Object.create(Person.prototype)

這里我們通過Object.createclone了一個新的prototype而不是直接把Person.prtotype直接賦值,因為引用關系眯娱,這樣會導致后續(xù)修改子類的prototype也修改了父類的prototype,因為引用關系修改的是同一個值试伙,如果是直接賦值就等于是Student.prototype.__proto__ = Person.prototype

Object.create是ES5方法于样,之前版本通過遍歷屬性也可以實現(xiàn)淺拷貝

注意:對子類添加新的方法,必須在修改其prototype之后蚤蔓,否則會被覆蓋掉

最后需要重新指定一下constructor屬性到自己的類型

所以糊余,最后寫為

function Person(name, age){
   this.name = name;
   this.age = age;
}

Person.prototype.sayName = function(){
   console.log('My name is'+this.name);
};
Person.prototype.walk = function(){
   console.log(this.name+'is walking')
}
var p = new Person('ruoyu',100)

function Student(name, age,sex){
   //把屬性拿過來
   Person.call(this,name,age)
   //Person.bind(this,name,age)
   this.sex = sex
}
//方法拿過來
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student //重新指定一下constructor屬性到自己的類型 ,不然Student.prototype.constructor = Person啦
Student.prototype.doing = function(){
   console.log("I am studying");
};

var s = new Student('hunger',2,'boy')
#繼承過后的a既是Student的對象 也是Person的對象
s instanceof Student//true
//s._proto_ ===Student.prototype
s instanceof Person //true
//s._proto_._proto_ === Person.prototype

另一種方法贬芥,把方法的繼承封裝成函數(shù)inherit

function inherit(superType, subType){
    var _prototype  = Object.create(superType.prototype);
    _prototype.constructor = subType;
    subType.prototype = _prototype;
}
#使用方法
function Person(name, sex){
    this.name = name;
    this.sex = sex;
}

Person.prototype.printName = function(){
    console.log(this.name);
};    

function Male(name, sex, age){
    Person.call(this, name, sex);
    this.age = age;
}
inherit(Person, Male);

// 在繼承函數(shù)之后寫自己的方法,否則會被覆蓋
Male.prototype.printAge = function(){
    console.log(this.age);
};

var m = new Male('Byron', 'm', 26);
m.printName();
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末昏苏,一起剝皮案震驚了整個濱河市威沫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孵构,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浦译,死亡現(xiàn)場離奇詭異,居然都是意外死亡精盅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門谜酒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叹俏,“玉大人僻族,你說我怎么就攤上這事粘驰。” “怎么了蝌数?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長度秘。 經(jīng)常有香客問我顶伞,道長剑梳,這世上最難降的妖魔是什么唆貌? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮垢乙,結果婚禮上锨咙,老公的妹妹穿的比我還像新娘。我一直安慰自己追逮,他們只是感情好酪刀,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布钮孵。 她就那樣靜靜地躺著骂倘,像睡著了一般稠茂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上睬关,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音料睛,去河邊找鬼丐箩。 笑死摇邦,一個胖子當著我的面吹牛施籍,可吹牛的內(nèi)容都是我干的概漱。 我是一名探鬼主播丑慎,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓤摧!你這毒婦竟也來了竿裂?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤照弥,失蹤者是張志新(化名)和其女友劉穎腻异,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體这揣,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡悔常,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了曾沈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片这嚣。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塞俱,靈堂內(nèi)的尸體忽然破棺而出姐帚,到底是詐尸還是另有隱情,我是刑警寧澤障涯,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布罐旗,位于F島的核電站,受9級特大地震影響唯蝶,放射性物質發(fā)生泄漏九秀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一粘我、第九天 我趴在偏房一處隱蔽的房頂上張望鼓蜒。 院中可真熱鬧,春花似錦征字、人聲如沸都弹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畅厢。三九已至,卻和暖如春氮昧,著一層夾襖步出監(jiān)牢的瞬間框杜,已是汗流浹背浦楣。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咪辱,地道東北人振劳。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像油狂,于是被迫代替她去往敵國和親澎迎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 目錄1.this究竟是什么2.綁定this的方法3.caller选调、arguments和callee 1.this究...
    犯迷糊的小羊閱讀 642評論 0 0
  • What's this? 由于運行期綁定的特性,JavaScript 中的 this 含義非常多灵份,它可以是全局對象...
    cce117b0a0ce閱讀 320評論 0 0
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持仁堪,譯者再次奉上一點點福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠填渠,并抽取幸運大...
    HetfieldJoe閱讀 2,997評論 4 14
  • 踏著青春的臺階弦聂,乘著風,飛向想去的遠方氛什。 個性張揚的穿著莺葫,敢愛敢恨的直率,裙角飛舞的斑斕枪眉,互相取笑的表情……青春的...
    糖點什么閱讀 161評論 0 0
  • 悠然度過每天的24小時
    kwork1988閱讀 205評論 0 0