摘要:為什么用能夠動態(tài)的取值,實現(xiàn)了隱式傳遞一個對象的引用上面的例子中想在中取的值用就能簡單搞定一定程度上實現(xiàn)了動態(tài)取值這個就是綁定了,這個就是上下文對象也就是調(diào)用棧是什么始終是一個對象,函數(shù)在被調(diào)用時發(fā)生綁定,具體綁定了什么看調(diào)用的位置,有一定
this 為什么用this
this能夠動態(tài)的取值,實現(xiàn)了隱式傳遞一個對象的引用
var obj = { a : { b : 2, c : function () { console.log(this.b); } } }
上面的例子中想在c中取b的值用this就能簡單搞定,一定程度上實現(xiàn)了動態(tài)取值;obj.a.c(),這個this就是綁定了obj.a,這個就是上下文對象也就是調(diào)用棧
this是什么this始終是一個對象,函數(shù)在被調(diào)用時發(fā)生綁定,具體綁定了什么看調(diào)用的位置,有一定的綁定規(guī)則
綁定規(guī)則
默認(rèn)綁定
當(dāng)函數(shù)不帶任何修飾被調(diào)用時,運用默認(rèn)綁定,this就是window全局對象;但要注意的是在嚴(yán)格模式下,則不行,this會綁定成undefined
function f() { //"use strict" console.log(this.a); } var a = 123; f(); //123
function f() { "use strict" console.log(this.a); } var a = 123; f(); //報錯,undefined不能夠添加屬性
隱式綁定
當(dāng)上下文對象(調(diào)用棧)調(diào)用函數(shù)時,會隱式綁定this,哪個對象調(diào)用就綁定誰
function f() { console.log(this.a); } var obj = { a : 123, b : f, } obj.b();
上面例子實際上就是this = obj,而不是this = obj.a
這是最簡單的,但是這種綁定常常會發(fā)生隱式丟失,而采用默認(rèn)綁定
function f() { console.log(this.a); } var obj = { a : 123, b : f, } var a = 345 var f1 = obj.b; //采取函數(shù)別名而發(fā)生了隱式丟失 f1(); //345,這等同于直接執(zhí)行了f函數(shù)
我的理解是函數(shù)取別名是最好賦值上下文對象,也就是調(diào)用棧(var f1 = b)
例如下面例子也是隱式丟失
function f() { console.log(this.a); } var obj = { a : 123, b : f, } function fn(f) { //等同于setTimeout(obj.b, 100) f(); } fn(obj.b);
要注意這種別名:
function f() { console.log(this.a); } var obj = {a : 1}; obj.fn = f; obj.fn()
實際上就是:
function f() { console.log(this.a); } var obj = { a : 1, fn : f, }; obj.fn()
顯示綁定
javascript提供了3種call,apply,bind;作用都是改變this的指向
call
可以填入多個參數(shù)
func.call(thisValue, arg1, arg2, arg3,...)
第一個參數(shù)是this要綁定的對象,若參數(shù)為空,undefined,null,則默認(rèn)綁定window;若參數(shù)為原始值,則this綁定對應(yīng)的包裝類對象
后面參數(shù)則是傳入func的參數(shù)
function f(n, m) { console.log(n, m, this.a); } var obj = {a : 1}; f.call(obj, 3, 4); //3 4 1
function f() { console.log(this); } f.call("123"); //String?{"123"}
apply
apply與call類似只是參數(shù)不同,就是將傳入原函數(shù)的參數(shù)變成數(shù)組形式
func.call(thisValue, [arg1, arg2, arg3,...])
apply與call可以這樣理解:就是thisValue借用func函數(shù)功能來實現(xiàn)效果
類似于thisValue.fn(arg1, arg2, arg3...)
利用這一特點可以實現(xiàn)有趣的效果
找數(shù)組中最大的數(shù)
var arr = [11, 2, 3, 4, 5]; var nmax = Math.max.apply(null, arr); //11
null的作用是因為用不到this,用null代替,僅用后面的參數(shù),有柯里化的感覺
建議我們一般定義一個比null還空的空對象:var ? = Object.create(null)
將數(shù)組中的空元素變成undefined,這個樣就可遍歷了,其屬性描述的enumerable:true
var arr = [11, , 3, 4, 5]; var arr1 = Array.apply(null, arr); //[11, undefined, 3, 4, 5]
轉(zhuǎn)換類數(shù)組對象,利用Array的實例方法slice,前提是必須有l(wèi)ength屬性
var arr = Array.prototype.slice.apply({0 :1, 1 : 2, length : 3}); //[1, 2, empty] var arr = Array.prototype.slice.apply({0 :1}); //[] var arr = Array.prototype.slice.apply({length : 1}); //[empty]
bind
bind函數(shù)參數(shù)形式和call類似
func.bind(thisValue, arg1, arg2, arg3,...)
我對他的理解是對原函數(shù)進(jìn)行改造,生成自己的新函數(shù),主要改造就是this綁定成thisValue,并且可以固定部分參數(shù),當(dāng)然后面arg選填
類似于thisValue.fn
我們可以自定義一個簡單bind函數(shù):
function myBind(fn, obj) { return function () { fn.apply(obj, arguments); } } function f(n) { console.log(n + this.a) } var obj = {a : 1}; var bind = myBind(f, obj); bind(4); //5
原生態(tài)bind用法也類似
function f(n,m) { console.log(this.a + n + m); } var obj = {a : 1}; var bar = f.bind(obj, 2); bar(3); //6
注意點bind每次返回一個新函數(shù),在監(jiān)聽事件時要注意,不然romove不掉
element.addEventListener("click", o.m.bind(o)); element.removeEventListener("click", o.m.bind(o));
因為o.m.bind(o)返回的時新函數(shù),所以remove的也不是開啟時的函數(shù)了
正確做法:添加一個值,記錄開啟時的函數(shù)
var listener = o.m.bind(o) element.addEventListener("click", listener); element.removeEventListener("click", listener);
有趣的地方:
利用call與bind實現(xiàn)原始方法
var slice = Function.prototype.call.bind(Array.prototype.slice); slice([1, 2, 3], 1); //[2, 3]
可以拆分bind看
Array.prototype.slice.Function.prototype.call([1, 2, 3], 1)
其中Function.prototype.call 就是call方法
Array.prototype.slice.call([1, 2, 3], 1)
拆分call
[1, 2, 3].Array.prototype.slice(1)
而Array.prototype.slice就是slice
所以
[1, 2, 3].slice(1)
個人理解可以看成,Array.prototype.slice實現(xiàn)了slice功能,F(xiàn)unction.prototype.call實現(xiàn)了arguments中this的綁定以及參數(shù)的帶入。所以函數(shù)最總調(diào)用時顯示:slice([1, 2, 3], 1);
同理
var push = Function.prototype.call.bind(Array.prototype.push); var pop = Function.prototype.call.bind(Array.prototype.pop);
同時bind也能被改寫
function f() { console.log(this.a); } var obj = { a : 123 }; var bind = Function.prototype.call.bind(Function.prototype.bind); bind(f, obj)() // 123
new的this綁定
當(dāng)用new來指向函數(shù)F時,函數(shù)變成構(gòu)造函數(shù)F,this也會發(fā)生變化
this = Object.create(F.prototype)
具體new的功能可看我的new篇
new綁定 > 顯示綁定 > 隱式綁定 > 默認(rèn)綁定
隱式綁定與默認(rèn)綁定比較
function f() { console.log(this.a); } var obj = { a : 123, f : f, } var a = 456; obj.f(); // 123
obj.f()覆蓋了f(),因此隱式大于默認(rèn)
隱式綁定與顯示綁定比較
function f() { console.log(this.a); } var obj = { a : 123, f : f, } var obj1 = { a : 1123 } obj.f.call(obj1); //1123
由輸出結(jié)果可以看出:call綁定的obj1覆蓋了obj,所以顯示大于隱式
顯示綁定與new綁定
function f(n) { this.a = n; console.log(this); } var obj = { b : 2}; var bar = f.bind(obj); console.log(bar(2)); console.log(new bar(2)); //{b:2,a:2} //undfined //{a: 2} //{a: 2}
由輸出結(jié)果可以看出:new bar(2),{a:2}說明是新生成的空對象,添加了a的屬性,其次輸出兩個說明函數(shù)返回了一個this對象也就是{a:2},而不是undefined
所以new大于顯示
注意多層this*
var obj = { f: function () { console.log(this); var f1 = function () { //console.log(this); }(); } } obj.f(); //{f:func} //winodw
因為傳統(tǒng)的this沒有繼承機制,所以這個匿名函數(shù)的this沒有任何修飾,采取默認(rèn)綁定
有兩種方法解決
var obj = { f: function () { var self= this console.log(self); var f1 = function () { //console.log(self); }(); } } obj.f(); //{f:func} //{f:func}
var obj = { f: function () { console.log(this); return () => { console.log(this); }; } } obj.f()(); //{f:func} //{f:func}
注意處理數(shù)組時使用this
var obj = { a : 123, arr : [1, 2, 3], f : function () { this.arr.forEach(function (elem) { console.log(this.a, elem); }); } } var a = "this是window"; obj.f(); //this是window 1 //this是window 2 //this是window 3
forEach中的this指向的是window
解決辦法:
var obj = { a : 123, arr : [1, 2, 3], f : function () { this.arr.forEach(function (elem) { console.log(this.a, elem); },this); } } var a = "this是window"; obj.f(); //123 1 //123 2 //123 3
還有一種就是賦值給一個變量self暫時保存,給遍歷時用,如同上面的self
注意回調(diào)函數(shù)中的this
var obj = new Object(); obj.f = function () { console.log(this === obj); } $("#button").on("click", obj.f); //false
this指向的是DOM對象而不是obj,這個有點難以察覺,需要注意
解決辦法:硬綁定obj對象
var obj = new Object(); obj.f = function () { console.log(this === obj); } function fn(){ obj.f.apply(obj); } $("#button").on("click", fn); //true //$("#button").on("click", o.f.bind(obj)); //true
上面的硬綁定相當(dāng)于固定了this的對象,不會變了。
我們可以做個軟綁定
if(!Function.prototype.sorfBind) { Function.prototype.sorfBind = function (obj) { //這個this:執(zhí)行sorfBind時的調(diào)用位置綁定的this var fn = this; var arg1 = [].slice.call(arguments, 1); var bound = function() { //這個this:執(zhí)行bound時綁定的this return fn.apply( //arg1.concat.call(arg1, arguments 作用是整合兩次執(zhí)行傳入的參數(shù) //(!this || this === (window || global)) 猜測是為了在嚴(yán)格模式下也試用 (!this || this === (window || global)) ? obj : this, arg1.concat.call(arg1, arguments) //arguments是bound函數(shù)執(zhí)行時參數(shù)的參數(shù), ); }; bound.prototype = Object.create(fn.prototype); return bound; } } function f() { console.log("name: " + this.name); } var obj = {name : "obj"}, obj1 = {name : "obj1"}, obj2 = {name : "obj2"}; var fObj = f.sorfBind(obj); fObj(); //name : obj obj1.foo = f.sorfBind(obj); obj1.foo(); //name : obj1 fObj.call(obj2); //name : obj2 setTimeout(obj1.foo, 10); //name : obj簡單談?wù)劶^函數(shù)對this的影響
初次綁定this后,箭頭函數(shù)內(nèi)部的this就固定了
function f() { return (b) => { console.log(this.a); } } var obj = {a : 1}; var obj1 = {a : 2}; var fn = f.call(obj); fn(); //1 fn.call(obj1); //1
與第一點類似,初次綁定后內(nèi)部this固定了也就有了繼承
function f() { setTimeout(() => { console.log(this.a); },10); } var obj = {a : 1}; f.call(obj); //1還有一點,不建議使用的屬性也會改變this的指向
arguments.callee 也有改變this指向
function f() { console.log(this); if(this ==window) { arguments.callee(); } } f(); //window對象 //arguments對象
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98163.html
摘要:命令作用作用是執(zhí)行構(gòu)造函數(shù),返回實例對象上面例子是自定義一個構(gòu)造函數(shù),其最大的特點就是首字母大寫,用執(zhí)行構(gòu)造函數(shù)其中,在的執(zhí)行下,代表了實例化后的對象,這個也就有屬性注意點如果不用執(zhí)行構(gòu)造函數(shù),那么指向的是全局有兩種方式可以避免內(nèi)部定義嚴(yán)格 new命令 new作用 作用是執(zhí)行構(gòu)造函數(shù),返回實例對象 function F() { this.name = object } var ...
摘要:原型要掌握這三者之間的關(guān)系,通過代碼例子記錄一下自身屬性的這里就是通過代碼看一下做了什么默認(rèn)情況下,將的所有屬性包括繼承的賦值給有什么東西呢自己的原型鏈,添加一個屬性,用來指明對象的誰構(gòu)造的自身全部屬性,這邊構(gòu)建一個空對象原型,所以沒有自有 原型 要掌握這三者之間的關(guān)系prototype,constructor,__proto__通過代碼例子記錄一下 function F() { ...
摘要:對象拷貝可遍歷屬性淺拷貝簡單的淺拷貝可以用,對存取器定義的對象也試用深拷貝屬性描述對象的拷貝這是個淺拷貝深拷貝不可遍歷屬性對象的拷貝例如拷貝獲得共同的原型,與是兄弟關(guān)系說明也繼承了原型,同級別簡潔化版 對象拷貝 可遍歷屬性 淺拷貝 if(typeof Object.prototype.copy != function) { Object.prototype.copy = fun...
摘要:它不區(qū)分該屬性是對象自身的屬性,還是繼承的屬性。那么我們要遍歷對象所有屬性,包括繼承以及不可遍歷的屬性,用加原型遍歷實現(xiàn)類似的用遞歸 Object靜態(tài)方法 Object自身方法,必須由Object調(diào)用,實例對象并不能調(diào)用 Object.getPrototypeOf() 作用是獲取目標(biāo)對象的原型 function F() {}; var obj = new F(); console.lo...
摘要:繼承是面向?qū)ο缶幊陶Z言中的一個重要的概念,繼承可以使得子類具有父類的屬性和方法或者重新定義追加屬性和方法等。但是在中沒有類的概念,是基于原型的語言,所以這就意味著對象可以直接從其他對象繼承。 繼承是面向?qū)ο缶幊陶Z言中的一個重要的概念,繼承可以使得子類具有父類的屬性和方法或者重新定義、追加屬性和方法等。但是在Javascript中沒有類的概念,是基于原型的語言,所以這就意味著對象可以直接...
摘要:在中,除了幾種原始類型外,其余皆為對象,,既然對象如此重要,那就列舉一下在中如何創(chuàng)建對象通過構(gòu)造函數(shù)創(chuàng)建對象實例對象字面量對象字面量是對象定義的一種簡寫形式,目的在于簡化創(chuàng)建包含大量屬性的對象的過程。 在Javascript中,除了幾種原始類型外,其余皆為對象(Object,Array ...),既然對象如此重要,那就列舉一下在Javascript中如何創(chuàng)建對象: 通過Object構(gòu)造...
閱讀 816·2021-11-12 10:36
閱讀 3409·2021-09-08 10:44
閱讀 2767·2019-08-30 11:08
閱讀 1429·2019-08-29 16:12
閱讀 2695·2019-08-29 12:24
閱讀 921·2019-08-26 10:14
閱讀 709·2019-08-23 18:32
閱讀 1202·2019-08-23 17:52