ES5中this的指针

按照this指针的优先级,列出下面常会遇到的四种情况,从上到下依次是优先级从高到低(后面会详细比较优先级)。

  1. 函数是和new一起被调用的吗(new绑定)?如果是,this就是新构建的对象。

    var bar = new foo()

  2. 函数是用callapply被调用(明确绑定),甚至是隐藏在bind 硬绑定 之中吗?如果是,this就是明确指定的对象。

    var bar = foo.call( obj2 )

  3. 函数是用环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this就是那个环境对象。

    var bar = obj1.foo()

  4. 否则,使用默认的this默认绑定)。如果在strict mode下,就是undefined,否则是global对象。 var bar = foo()

以上,就是理解对于普通的函数调用来说的this绑定规则所需的全部。是的···几乎是全部。

apply、call、bind

因为apply、call存在于Function.prototype中,所以每个方法都有这两个属性。

  • call
函数名.call(对象,arg1....argn)
//功能:
//1.调用函数
//2.将函数内部的this指向第一个参数的对象
//3.将第二个及以后所有的参数,作为实参传递给函数
  • apply
    主要用途是直接用数组传参
函数名.apply(对象, 数组/伪数组);
//功能:
//1.调用函数
//2.将函数内部的this指向第一个参数的对象
//3.将第二个参数中的数组(伪数组)中的元素,拆解开依次的传递给函数作为实参
//案例求数组中最大值
var a=Math.max.apply( null, [ 1, 2, 5, 3, 4 ] );
console.log(a);// 输出:5
  • bind
  1. 是创建一个新的函数,我们必须要手动去调用:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)() // 3
  • 注意事项

    1. call和apply功能几乎一致,唯一的区别就是传参的方式!!
      2.call和apply的第一个参数如果为null或者undefined,那么this指向window
      3.call和apply的第一个参数如果为值类型的数据,那么会将这个值类型的数据,转换成其对应的引用类型的数据,然后再将this指向这个引用类型的数据
      4.call和apply立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。
      5.当参数的个数确定的情况下可以使用call;当参数的个数不确定的情况下可以使用apply

apply应用

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] */

call应用(将伪数组转为数组)

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"]

call应用(判断复杂数据类型)

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]'

bind在react中应用

下面的例子是来自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 to onClickthis will be undefined when the function is actually called.

ES6中this的指向

箭头函数this指向注意事项

箭头函数体内的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. 在attchEvent里面的this指向的不是事件的调用者,而是window(奇葩),而addEventListener指向的事件调用者。
  2. attachEvent的type一定要加上on,不然没效果

面试题

下面的面试题一、二、四都设计到引用问题,如果不是很好理解还可以理解成在 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绑定 和 明确绑定)

this 指向问题ES5的更多相关文章

  1. this指向和apply,call,bind三者的区别

    一.前言 this指向,apply,call,bind的区别是一个经典的面试问题,同时在项目中会经常使用到的原生的js方法.同时也是ES5中的众多坑的一个.ES6中可能会极大的避免了this产生的错误 ...

  2. ES5中的this

    参考资料:>>> this的指向 在 ES5 中,其实 this 的指向,始终坚持一个原理: this 永远指向最后调用它的那个对象 下面我们来看一个最简单的例子:(例子均来自参考资 ...

  3. 译文:javascript function中的this

    个人理解+google翻译.如有错误,请留言指正.原文来自MDN: this 简介 Javascript中一个函数的this关键字的行为相对其它语言有些不同.在严格模式和非严格模式间也有区别. 在大多 ...

  4. javascirpt之 this、apply、call、bind

    this.apply.call.bind 这又是一个面试经典问题~/(ㄒoㄒ)/~~也是 ES5中众多坑中的一个,在 ES6 中可能会极大避免 this 产生的错误,但是为了一些老代码的维护,最好还是 ...

  5. typescript - 3.函数

    (1)函数的定义 ## 函数声明法 // function run():string{ // return 'run'; // } //错误写法,返回类型错误 // function run():st ...

  6. Vue小白篇 - ES6的语法

    为什么要学 ES6 的语法呢? 因为 Vue 语法有很多都是 ES6(ECMAScript 6),这里推荐 [阮一峰 ECMAScript 6 入门]: http://es6.ruanyifeng.c ...

  7. ES5与ES6 this 指向详细解析(箭头函数)

    首先要明白箭头函数的作用: 箭头函数除了让函数的书写变得很简洁,可读性很好外:最大的优点是解决了this执行环境所造成的一些问题.比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性) ...

  8. ES5中改变this指向的三种方法

    ES5中提供了三种改变函数中this指针指向的方法,分别如下 1.call() var obj = {username:"孙悟空"}; //没有任何修饰的调用函数,函数中的this ...

  9. ES5 常用 语法(object Arrary 函数绑定this指向)

    ES object 扩展 ES object 扩展1. <!DOCTYPE html> <html> <head> <link rel="short ...

随机推荐

  1. Maven配置本地库加载ojdbc14-10.2.0.4.0.jar文件

    因为ojdbc14-10.2.0.4.0.jar是要收费的,所以Maven的远程库里没有这个资源,只能通过配置本地库来加载到J2EE工程里,做法如下:   第一步:在网上下载ojdbc14.jar,然 ...

  2. rlwrap与历史命令

    # yum install rlwrap $ vi .bash_profile alias sqlplus='rlwrap sqlplus'alias rman='rlwrap rman' 或者 l ...

  3. pycharm2017自建注册服务器

    root运行: wget http://home.ustc.edu.cn/~mmmwhy/jetbrain.sh && sh ./jetbrain.sh 注意:需要安装screen软件 ...

  4. git回滚命令reset、revert的区别

    ##使用git,总有一天会遇到下面的问题: (1)改完代码匆忙提交,上线发现有问题,怎么办? 赶紧回滚. (2)改完代码测试也没有问题,但是上线发现你的修改影响了之前运行正常的代码报错,必须回滚. 所 ...

  5. P1359 租用游艇

    题目描述 长江游艇俱乐部在长江上设置了n 个游艇出租站1,2,…,n.游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇.游艇出租站i 到游艇出租站j 之间的租金为r(i,j),1& ...

  6. js获取图片的原始尺寸

    <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  7. [SPOJ2939]Qtree5

    [SPOJ2939]Qtree5 Tags:题解 题意 链接 给你\(n\)个节点的黑白树,初始全黑.每次可以翻转某点颜色,或查询距离某点最近的白点的距离.\(n\le 10^5\).强制LCT,不准 ...

  8. tkinter 弹出窗口 传值回到 主窗口

    有些时候,我们需要使用弹出窗口,对程序的运行参数进行设置.有两种选择 一.标准窗口 如果只对一个参数进行设置(或者说从弹出窗口取回一个值),那么可以使用simpledialog,导入方法: from ...

  9. FormData 数据转化为 json 数据

    两种方法 <!-- 实例:将 FormData 转化为 json --> <meta charset="utf-8"/> <form enctype= ...

  10. idea ssm项目迁移到另一台机器上时出现不能正常启动项目的解决方案

    首先右下角提示关联spring文件,关联之,然后启动,发现项目无法启动,然后开始排错 首先从这个日志里发现了这么一条提示信息 然后百度了一下,答案都是说 web.xml 之类的 spring拦截器问题 ...