按照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.typescript
箭头函数体内的this
对象,若是包裹在函数中就是函数调用时所在的对象,若是放在全局中就是指全局对象window。而且固定不会更改。换句话说内部的this
就是外层代码块的this
json
下面是对比分析普通函数和箭头函数中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绑定 和 明确绑定)