JS的关键字this
1.关于this
this关键字是JavaScript中最复杂的机制之一。它是一个很特别的关键字,被自动定义在所有函数的作用域中。
1.1 为什么要用this?
this
提供了一种更优雅的方式来隐式地“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。
function identify(){
return this.name.toUpperCase();
}
function speak(){
var greeting = "Hello, I'm " + identify.call(this);
console.log(greeting);
}
var me = {
name : "Kyle"
};
var you = {
name : "Reader"
};
console.log(identify.call(me));//KYLE
console.log(identify.call(you));//READER
speak.call(me);//Hello, I'm KYLE
speak.call(you);//Hello, I'm READER
如果不使用this
,那就需要给identify()
和speak()
显式传入一个上下文对象。
function identify(context){
return context.name.toUpperCase();
}
function speak(context){
var greeting = "Hello, I'm " + identify(context);
console.log(greeting);
}
var me = {
name : "Kyle"
};
var you = {
name : "Reader"
};
console.log(identify(you));//READER
speak(me);//Hello, I'm KYLE
1.2 误解
人们很容易把this
理解成指向函数自身。事实上,this
并不是指向函数本身。
如果要从函数对象内部引用它自身,那只使用this
是不够的。一般来说你需要通过一个指向函数对象的词法标识符(变量)来引用它。
function foo(){
foo.count = 4;//foo指向它自身
}
setTimeout(function(){
//匿名函数无法指向自身
},10);
在需要自引用时使用具名函数(表达式)。
function foo(num){
console.log("foo: " + num);
this.count++;
}
foo.count = 0;
var i;
for(i=0; i<10; i++){
if(i > 5){
//使用call(..)可以确保this指向函数对象foo自身
foo.call(foo,i);
}
}
//foo: 6
//foo: 7
//foo: 8
//foo: 9
console.log(foo.count);//4
1.3 this到底是什么
this
是 在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this
的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
2.this全面解析
2.1 调用位置
function baz(){
//当前调用栈是:baz
//因此,当前调用位置是全局作用域
console.log("baz");
bar();//<--bar的调用位置
}
function bar(){
//当前调用栈是:baz -> bar
//因此,当前调用位置在baz中
console.log("bar");
foo();//<--foo的调用位置
}
function foo(){
//当前调用栈是:baz -> bar -> foo
//因此,当前调用位置在bar中
console.log("foo");
}
baz();//<-- baz的调用位置
可以使用浏览器的开发者工具查看调用栈。
2.2 绑定规则
默认绑定
function foo(){
console.log(this.a);
}
var a = 2;
foo();//2
this
的默认绑定:this
指向全局对象。
foo()
是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。
如果使用严格模式(strict模式),则不能将全局对象用于默认绑定,因此this
会绑定到undefined。
通常来说你不应该在代码中混合实用strict模式和非strict模式。
隐式绑定
function foo(){
console.log(this.a);
}
var obj = {
a : 2,
foo : foo
};
obj.foo();//2
当foo()
被调用时,它的前面加上了对obj的引用。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this
绑定到这个上下文对象。因为调用foo()
时this
被绑定到obj,因此this.a
和obj.a
是一样的。
function foo(){
console.log(this.a);
}
var obj = {
a : 2,
foo : foo
};
var bar = obj.foo;
var a = "oops, global";
bar();//oops, global
虽然bar是obj.foo
的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()
其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
显式绑定
在某个对象上强制调用函数,可以使用函数的call(...)
和apply(...)
方法。
JavaScript提供的绝大多数函数以及你自己创建的所有函数都可以使用call(...)
和apply(...)
方法。
function foo(){
console.log(this.a);
}
var obj = {
a : 2
};
foo.call(obj);//2
通过foo.call(...)
,我们可以在调用foo时强制把它的this
绑定到obj上。
如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作this
的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(..)
、new Boolean(..)
或者new Number(..)
)。这通常被称为“装箱”。
硬绑定
function foo(){
console.log(this.a);
}
var obj = {
a : 2
};
var bar = function(){
foo.call(obj);
};
bar();//2
setTimeout(bar,100);//2
//硬绑定的bar不可能再修改它的this
bar.call(window);//2
硬绑定是一种非常常用的模式,ES5提供了内置的方法Function.prototype.bind
。
function foo(something){
console.log(this.a,something);
return this.a + something;
}
var obj = {
a : 2
};
var bar = foo.bind(obj);
var b = bar(3);//2 3
console.log(b);//5
new 绑定
在JavaScript中,构造函数只是一些使用new操作符时被调用的函数,它们并不会属于某个类,也不会实例化一个类。
包括内置对象函数在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
- 创建(或者说构造)一个全新的对象。
- 这个新对象会被执行[[Prototye]]连接。
- 这个新对象会绑定到函数调用的
this
。 - 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);//2
2.3 优先级
默认绑定的优先级是四条规则中最低的。
显式绑定的优先级比隐式绑定的优先级高。
new绑定比隐式绑定优先级高。
硬绑定(显式绑定的一种)比new绑定的优先级高。
我们可以根据优先级来判断函数在某个调用位置应用的是哪条规则:
- 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
- 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
- 函数在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。
2.4 绑定例外
如果你把null
或者undefined
作为this
的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的时候默认绑定规则。
function foo(){
console.log(this.a);
}
var a = 2;
foo.call(null);//2
总是使用null
来忽略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
赋值表达式p.foo = o.foo
的返回值是目标函数的引用,因此调用位置是foo()
而不是p.foo()
或者o.foo()
,所以这里会应用默认绑定。
JS的关键字this的更多相关文章
- js 中关键字 this的用法
<1> js中this 的用法? (key:函数是由调用的,四种情况标红可知) (http://www.ruanyifeng.com/blog/2010/04/using_this_k ...
- js基础 1.简单js 语法 关键字 保留字 变量
简单js JavaScript 是一个松散性的语言 对象属性却不想c中的结构体或者c++ 和java的对象, 对象继承机制 使用原型的prototype(原型链),js的分为三部分ECMAScript ...
- day1——js方法关键字的问题(onclick点了没反应)
<a href="javascript:void(0);" onclick="search();" >提交</a> js代码: fu ...
- js常用关键字和函数
document.createElement("div"): 创建一个div元素申明一个变量 document.body.appendChild(div); 将创建好的div添 ...
- js 查找关键字
查找:4种: 1. 查找固定关键字,仅返回位置,可指定开始位置: var i=str.indexOf("kword"[,starti]); str.lastIndexOf(&quo ...
- JavaScript高级程序设计(五): js的关键字instanceof和typeof使用
JavaScript中instanceof和typeof 常用来判断一个变量是否为空,或者是什么类型的.但它们之间还是有区别的: 一.typeof 1.含义:typeof返回一个表达式的数据类型的字符 ...
- ASP.NET给前端动态添加修改 CSS样式JS 标题 关键字
有很多网站读者能换自己喜欢的样式,还有一些网站想多站点共享后端代码而只动前段样式,可以采用动态替换CSS样式和JS. 如果是webform 开发,可以用下列方法: 流程是首先从数据中或者xml读取数据 ...
- [js]this关键字代表当前执行的主体
点前是谁,this就是谁 <div id="div1" class="div1"></div> <div id="div ...
- js new关键字
实现new 关键字只需4步 1. 声明一个对象: 2. 把这个对象的__proto__ 指向构造函数的 prototype; 3. 以构造函数为上下文执行这个对象: 4. 返回这个对象. 简洁的代码示 ...
随机推荐
- 企业服务总线ESB
# 企业服务总线ESB 由中间件技术实现并支持SOA的一组基础架构,支持异构环境中的服务.消息以及基于事件的交互,并且具有适当的服务级别和可管理性. 通过使用ESB,可以在几乎不更改代码的情况下,以一 ...
- Protocol buffer的使用案例
Protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台.google 提供了多种语言的实现:java.c#.c++.go 和 python,每一种实 ...
- codeforces 1140E Palindrome-less Arrays
题目链接:http://codeforces.com/contest/1140/problem/E 题目大意: 如果一个数组的存在一个奇数长的回文就不好. 不是不好的数组是好的. 你可以把-1用1到k ...
- 主成分分析——PCA
在数据挖掘过程中,当一个对象有多个属性(即该对象的测量过程产生多个变量)时,会产生高维度数据,这给数据挖掘工作带来了难度,我们希望用较少的变量来描述数据的绝大多数信息,此时一个比较好的方法是先对数据进 ...
- python 根据年月日,计算是这一年中的第几天
利用python计算某一天是这一年中的第几天,例如,给定年份= 2019年,月份= 1,日期= 3,则返回3:因为2019-01-03日期是2019年的第3 天. 首先,我们要知道闰年.平年怎么区分: ...
- hadoop 集群HA高可用搭建以及问题解决方案
hadoop 集群HA高可用搭建 目录大纲 1. hadoop HA原理 2. hadoop HA特点 3. Zookeeper 配置 4. 安装Hadoop集群 5. Hadoop HA配置 搭建环 ...
- umask命令详解
基础命令学习目录首页 原文链接:https://blog.csdn.net/stpeace/article/details/45509425 umask命令用得相对不多, 而umask函 ...
- [redis] 几种redis数据导出导入方式
环境说明: 172.20.0.1 redis源实例 172.20.0.2 redis目标实例 172.20.0.3 任意linux系统 一.redis-dump方式 1.安装redis-dump工具 ...
- python之爬虫_并发(串行、多线程、多进程、异步IO)
并发 在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢 import requests def fetch_async(url): res ...
- 20162325 金立清 S2 W7 C16
20162325 2017-2018-2 <程序设计与数据结构>第7周学习总结 教材学习内容概要 树是非线性结构,其元素组织为一个层次结构 树的度表示树种任意结点的最大子结点数 有m个元素 ...