按照this
指針的優先級,列出下面常會遇到的四種狀況,從上到下依次是優先級從高到低(後面會詳細比較優先級)。javascript
new
一塊兒被調用的嗎(new綁定)?若是是,this
就是新構建的對象。var bar = new foo()
html
call
或apply
被調用(明確綁定),甚至是隱藏在bind
硬綁定 之中嗎?若是是,this
就是明確指定的對象。var bar = foo.call( obj2 )
java
this
就是那個環境對象。var bar = obj1.foo()
react
this
(默認綁定)。若是在strict mode
下,就是undefined
,不然是global
對象。 var bar = foo()
以上,就是理解對於普通的函數調用來講的this
綁定規則所需的所有。是的···幾乎是所有。git
由於apply、call存在於Function.prototype中,因此每一個方法都有這兩個屬性。es6
函數名.call(對象,arg1....argn) //功能: //1.調用函數 //2.將函數內部的this指向第一個參數的對象 //3.將第二個及之後全部的參數,做爲實參傳遞給函數
函數名.apply(對象, 數組/僞數組); //功能: //1.調用函數 //2.將函數內部的this指向第一個參數的對象 //3.將第二個參數中的數組(僞數組)中的元素,拆解開依次的傳遞給函數做爲實參 //案例求數組中最大值 var a=Math.max.apply( null, [ 1, 2, 5, 3, 4 ] ); console.log(a);// 輸出:5
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.bind(a,1,2)() // 3
注意事項github
JavaScript var array1 = [12 , "foo" , {name "Joe"} , -2458]; var array2 = ["Doe" , 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1 值爲 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 } Array.prototype.join.call(arrayLike, '&'); // name&age&sex Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] // slice能夠作到類數組轉數組 Array.prototype.map.call(arrayLike, function(item){ return item.toUpperCase(); }); // ["NAME", "AGE", "SEX"]
console.log( Object.prototype.toString.call(num), Object.prototype.toString.call(str), Object.prototype.toString.call(bool), Object.prototype.toString.call(arr), Object.prototype.toString.call(json), Object.prototype.toString.call(func), Object.prototype.toString.call(und), Object.prototype.toString.call(nul), Object.prototype.toString.call(date), Object.prototype.toString.call(reg), Object.prototype.toString.call(error) ); // '[object Number]' '[object String]' '[object Boolean]' '[object Array]' '[object Object]' // '[object Function]' '[object Undefined]' '[object Null]' '[object Date]' '[object RegExp]' '[object Error]'
下面的例子是來自react官網的案例面試
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // This binding is necessary to make `this` work in the callback this.handleClick = this.handleClick.bind(this); } handleClick() { console.log(this); this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } } ReactDOM.render( <Toggle />, document.getElementById('root') );
If you forget to bind
this.handleClick
and pass it toonClick
,this
will beundefined
when the function is actually called.json
箭頭函數體內的this
對象,若是包裹在函數中就是函數調用時所在的對象,若是放在全局中就是指全局對象window。而且固定不會更改。換句話說內部的this
就是外層代碼塊的this
數組
下面是對比分析普通函數和箭頭函數中this區別
// 普通函數 function foo() { setTimeout(function() { console.log('id:', this.id); }); } var id = 21; foo.call({ id: 42 }); //21
// 箭頭函數 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); //42 // 上面的匿名函數定義時所在的執行環境就是foo函數,因此匿名函數內部的this執向始終會和foo函數的this執向保持一致,不會更改,如同下面的這個案例 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo(); //21(沒有用call)
// ES5普通函數模擬上面es6函數的執行過程 function foo() { var _this = this; setTimeout(function () { console.log('id:', _this.id); }, 100); }
call的做用就是將foo函數的執行環境從window改爲對象
{id: 42}
定時器的做用就是延遲執行當前函數的外部執行環境,不管有沒有設置延遲時間
普通函數解釋:定義時this指向函數foo
做用域,可是在定時器100毫秒以後執行函數時,此時this指向window對象
箭頭函數解釋:this始終指向定義時所在對象,也就是始終指向foo
做用域
進一步分析this
var handler = { id: '123456', init: function() { document.addEventListener('click', event => this.doSomething(event.type), false); }, doSomething: function(type) { console.log('Handling ' + type + ' for ' + this.id); } }; handler.init()// Handlingclickfor123456
箭頭函數的this始終指向handler
,若是是普通函數,this指向document
this
指向的固定化,並非由於箭頭函數內部有綁定this
的機制,實際緣由是箭頭函數根本沒有本身的this
,致使內部的this
就是外層代碼塊的this
。正是由於它沒有this
,因此也就不能用做構造函數。
在IE678裏面不支持addEventListener和removeEventListener,而是支持attchEvent和detachEvent兩個方法。
語法:target.attachEvent(「on」+type, listener);
attachEvent和addEventListener區別:
下面的面試題1、2、四都設計到引用問題,若是不是很好理解還能夠理解成在 es5 中,永遠是this 永遠指向最後調用它的那個對象。摘錄連接
下面涉及指針應用還有綁定之類的概念來自You Dont Konw JS連接
this.x = 9; // this refers to global "window" object here in the browser var module = { x: 81, getX: function() { return this.x; } }; module.getX(); // 81 var retrieveX = module.getX; retrieveX(); // returns 9 - The function gets invoked at the global scope // Create a new function with 'this' bound to module // New programmers might confuse the // global var x with module's property x var boundGetX = retrieveX.bind(module); boundGetX(); // 81
retrieveX
只是getX
函數的引用,也就是隻是getX
的一個指針(getX
的另外一個指針是module.getX
),因此retrieveX
仍是指向getX
函數自己的
和上面相似的案例,下面的func只是函數引用,因此即便在函數內部,仍是執行的函數自己,不受詞法做用域限制(箭頭函數則受限制)
document.getElementById( 'div1' ).onclick = function(){ console.log( this.id );// 輸出: div1 var func = function(){ console.log ( this.id );// 輸出: undefined } func(); }; //修正後 document.getElementById( 'div1' ).onclick = function(){ var func = function(){ console.log ( this.id );// 輸出: div1 } func.call(this); };
function foo() { console.log( this.a ); } var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); // 3 (p.foo = o.foo)(); // 2
var A = function( name ){ this.name = name; }; var B = function(){ A.apply(this,arguments); }; B.prototype.getName = function(){ return this.name; }; var b=new B('sven'); console.log( b.getName() ); // 輸出: 'sven'
確實,許多包中的函數,和許多在JavaScript語言以及宿主環境中的內建函數,都提供一個可選參數,一般稱爲「環境(context)」,這種設計做爲一種替代方案來確保你的回調函數使用特定的this而沒必要非得使用bind(..)。
舉例來講:
function foo(el) { console.log( el, this.id ); } var obj = { id: "awesome" }; // 使用`obj`做爲`this`來調用`foo(..)` [1, 2, 3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome
明確綁定 的優先權要高於 隱含綁定
function foo() { console.log( this.a ); } var obj1 = { a: 2, foo: foo }; var obj2 = { a: 3, foo: foo }; obj1.foo(); // 2 obj2.foo(); // 3 obj1.foo.call( obj2 ); // 3 obj2.foo.call( obj1 ); // 2
new綁定的優先級高於隱含綁定(new和call/apply不能同時使用,因此new foo.call(obj1)是不容許的,也就是不能直接對比測試 new綁定 和 明確綁定)