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