this指向最后调用该函数的对象

		// 示例一:
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this) // window
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a(); // 相当于 window.a()
console.log("outer:" + this) // outer:Window // 示例二:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this) // a
console.log(this.name); // Cherry
}
}
window.a.fn(); // 相当于 a.fn() // 示例三:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this) // window
console.log(this.name); // windowsName
}
}
var f = a.fn;
f(); // 相当于 window.f() // 示例四:
var name = "windowsName";
function fn() {
var name = 'Cherry';
console.log(this) // window
console.log(this.name); // windowsName
innerFunction(); }
function innerFunction () {
console.log(this.name); // windowsName
}
fn() // 相当于 window.fn()

匿名函数的 this 永远指向 window

注意,这里我们没有使用严格模式,如果使用严格模式的话,全局对象就是 undefined,会报错 Uncaught TypeError: Cannot read property 'name' of undefined。

	var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
console.log(this) // a
setTimeout(function () {// 匿名函数的 this 永远指向 window
console.log(this) //window
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function

改变 this 的指向的几种方法:

  1. 使用 ES6 的箭头函数
  2. 在函数内部使用 _this = this
  3. 使用 apply、call、bind
  4. new 实例化一个对象

1.箭头函数的 this 始终指向函数定义时的 this,而非执行时

箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined

	var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( () => {
console.log(this) // a,  不是window
this.func1()
},100);
}
};
a.func2() // Cherry

2.在函数内部使用 _this = this。先将调用这个函数的对象保存在变量 _this 中,然后在函数中都使用这个 _this,这样 _this 就不会改变

var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout( function() {
_this.func1()
},100);
}
};
a.func2() // Cherry

3.使用 apply、call、bind 函数也是可以改变 this 的指向。

3.1 apply

语法:fun.apply(thisArg[, argsArray])

含义:该方法调用一个函数, 使用该函数(fun)的方法,同时改变函数中this的指向为thisArg。

参数:具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数

thisArg:在 fun 函数运行时指定的 this 值。注意:指定的 this 值并不一定是该函数执行时真正的 this 值,如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。

argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。如果该参数的值为null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。

var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.apply(a),100); // 此处省略第二个参数
}
};
a.func2() // Cherry

3.2 call

语法:fun.call(thisArg[, arg1, arg2,....])

注:与apply用法类似,不同之处:第二个参数为参数列表,使用逗号分割

var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(a),100); // 此处省略第二个参数
}
};
a.func2() // Cherry

3.3 bind

 语法:fun.bind(thisArg[, arg1, arg2, ...])()

含义:bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列

// 示例一:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(a)(),100);
}
};
a.func2() // Cherry // 示例二:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)() // 3

对于不支持 bind 的浏览器,兼容性写法:

		Function.prototype.bind = Function.prototype.bind || function(context) {
var self = this;
// console.log(this, context)
return function() {
console.log(arguments); // console [3,4] if ie<6-8>
return self.apply(context, arguments); }
} // 调用
var obj = {
a: 1,
b: 2,
getCount: function(c, d) {
return this.a + this.b + c + d;
}
};
window.a = window.b = 0;
var func = obj.getCount.bind(obj)(3, 4);
var apply = obj.getCount.apply(obj, [3, 4]);
var call = obj.getCount.call(obj, 3, 4); console.log(func, apply, call) // 10 // 或者:
Function.prototype.bind = Function.prototype.bind || function( context ){
var self = this; // 保存原函数 - 调用者
   context = [].shift.call( arguments ), // 需要绑定的 this 上下文
   args = [].slice.call( arguments ); 剩余的参数转化为数组
return function(){ // 返回一个新的函数
return self.apply( context, [].concat.call( args, [].slice.call( arguments ) ) );
// 执行新的函数的时候,会把之前传入context当作新函数体内的this
// 并且组合两次分别传入的参数,作为新的函数的参数
}
};
// 调用
var obj = {
name: 'sven'
};
var func = function( a, b, c, d ){
alert ( this.name ); // sven
alert ( [ a, b, c, d ] ) // [ 1, 2, 3, 4 ]
}.bind( obj, 1, 2 ); // obj ==> context func( 3, 4 );

4.构造函数Sum,如果我们直接调用这个构造函数Sum(),那么这个this代表window对象;但是我们对它进行实例化var obj = new Sum(),这样this.a上的this代表当前对象obj;

function Sum(a, b){
console.log(this); // 1=>window; 2=>实例化对象obj
this.a = a;
this.b = b;
this.add = function(){
console.log(this) // obj.add()=>实例化对象obj
return this.a+this.b;
}
} // 1.直接调用:
Sum(2, 3);
console.log(a, b); // 2, 3
// 2.实例化:
var obj = new Sum(2, 3);
var num = obj.add();
console.log(num); // 5

new 的过程:

  1. 创建一个空对象 obj;
  2. 将新创建的空对象的隐式原型指向其构造函数的显示原型。
  3. 使用 call 改变 this 的指向
  4. 如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话,那么直接直接返回该对象。

    注:构造器显式地返回了一个 object 类型的对象,那么此次运算结果最终会返回这个对象;

    构造器不显式地返回任何数据,或者是返回一个非对象类型的数据才会返回这个构造器实例
function Person(name,age){
this.name = name
this.age = age
return [1,2,3]
} var p = new Person('taurus_wood', 20) // [1, 2, 3] // 数组的类型是对象 function Person2(name,age){
this.name = name
this.age = age
return 1
} var p2 = new Person2('taurus_wood', 20) // {name:'taurus_wood', age: 20}

代码显示实例化过程

var a = new myFunction("Li","Cherry");
// 伪代码表示:
new myFunction{
var obj = {};
obj.__proto__ = myFunction.prototype; //建立了obj对象的原型链:obj->Animal.prototype->Object.prototype->null
var result = myFunction.call(obj,"Li","Cherry");
return typeof result === 'obj'? result : obj;
} // 或者:
Function.method('new', function() {
// this指向Function对象
// that是构造器对象
var that = Object.create(this.prototype);
// other是调用构造器之后生成的对象,跟这行代码一个意思: var other = new Person()
var other = this.apply(that, arguments);
// 如果它返回的不一个对象,就返回该新对象
return (typeof other === 'object' && other) || that;
})

函数调用的方法:

  1. 作为普通函数调用
  2. 函数作为方法调用(作为对象的方法调用)
  3. 使用构造函数调用函数(作为构造器调用)
  4. 作为函数方法调用函数(call、apply)

1.单纯的函数调用

// 如上例:
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a(); // 单纯的函数调用
console.log("outer:" + this) // outer: Window

2.函数作为对象的方法调用‘

// 如上例:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(a)(),100);
}
};
a.func2() // Cherry 作为a对象的方法调用

3.使用构造函数调用函数

如上例

4.作为函数方法调用函数(call、apply)

在 JavaScript 中, 函数是对象。

JavaScript 函数有它的属性和方法。

call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身

JS - this 总结的更多相关文章

  1. Vue.js 和 MVVM 小细节

    MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对View 和 ViewModel 的双向数据绑定,这使得ViewModel 的状态改变可以自 ...

  2. js学习笔记:操作iframe

    iframe可以说是比较老得话题了,而且网上也基本上在说少用iframe,其原因大致为:堵塞页面加载.安全问题.兼容性问题.搜索引擎抓取不到等等,不过相对于这些缺点,iframe的优点更牛,跨域请求. ...

  3. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  4. JS调用Android、Ios原生控件

    在上一篇博客中已经和大家聊了,关于JS与Android.Ios原生控件之间相互通信的详细代码实现,今天我们一起聊一下JS调用Android.Ios通信的相同点和不同点,以便帮助我们在进行混合式开发时, ...

  5. jquery和Js的区别和基础操作

    jqery的语法和js的语法一样,算是把js升级了一下,这两种语法可以一起使用,只不过是用jqery更加方便 一个页面想要使用jqery的话,先要引入一下jqery包,jqery包从网上下一个就可以, ...

  6. 利用snowfall.jquery.js实现爱心满屏飞

    小颖在上一篇一步一步教你用CSS画爱心中已经分享一种画爱心的方法,这次再分享一种方法用css画爱心,并利用snowfall.jquery.js实现爱心满屏飞的效果. 第一步: 利用伪元素before和 ...

  7. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  8. JS正则表达式常用总结

    正则表达式的创建 JS正则表达式的创建有两种方式: new RegExp() 和 直接字面量. //使用RegExp对象创建 var regObj = new RegExp("(^\\s+) ...

  9. 干货分享:让你分分钟学会 JS 闭包

    闭包,是 Javascript 比较重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,很难从定义去理解它.因此,本文不会对闭包的概念进行大篇幅描述 ...

  10. JS核心系列:理解 new 的运行机制

    和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...

随机推荐

  1. VMware or VirtualBox+centos7 安装教程

    一.准备工作:1.首次安装虚拟机时,需要的准备工作:打开intel的虚拟技术服务(否则安装centos系统时会报错).重启电脑,显示log时进入BIOS服务(不同电脑进入BIOS方式不同,一般是进入l ...

  2. C# 获取当前活动网络连接mac地址

    IPAddress localIp = null; IPAddress[] ipArray; ipArray = Dns.GetHostAddresses(Dns.GetHostName()); lo ...

  3. 精通CSS:高级Web标准解决方案(第二版) 不明白的地方

    P47 在图3-14中,当把框1向左浮动时,它脱离文档流并且向左移动,直到它的左边缘碰到包含框的左边缘.因为它不在处于文档流中,所以它不占据空间,实际上覆盖住了框2,使框2从视图中消失. 我的疑问是, ...

  4. CentOS定时备份MySQL数据库

    1.编写备份脚本 vi /usr/sbin/mysql_dy_backup.sh #!/bin/bash #备份路径 BACKUP=/data/backup/sql/dy #当前时间 DATETIME ...

  5. Android IdleHandler 原理浅析

    IdleHandler:空闲监听器(就像我没事做了,在群里发了个表情,这时候其他人就知道我很闲了) 在每次next获取消息进行处理时,发现没有可以处理的消息(队列空,只有延时消息并且没到时间,同步阻塞 ...

  6. java:LeakFilling (Mybatis)

    1.实体类属性与数据库中字段名字不一样时,或者起别名时: TbOrderMapper.xml配置文件中,配置resultMap标签: 其它相同的标签也需要配,否则查询不出来对应数据. 2.一对一关联: ...

  7. wps字体缺失,问题

    启动WPS for Linux后,出现提示"系统缺失字体" . 出现提示的原因是因为WPS for Linux没有自带windows的字体,只要在Linux系统中加载字体即可. 具 ...

  8. 什么是token?你是怎么理解token?

    1.Token的引入: Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生. 2. ...

  9. bat默认以管理员身份运行

    在Bat文件内容前插入以下一段代码: @echo off :: BatchGotAdmin :------------------------------------- REM --> Chec ...

  10. ElasticSearch 7.3.0 查询、修改、删除 文档操作

    PUT chuyuan/_doc/ { "name":"xiaolin", , "sex":"F", "lov ...