1.关于this

this关键字是JavaScript中最复杂的机制之一。它是一个很特别的关键字,被自动定义在所有函数的作用域中。

1.1 为什么要用this?

this提供了一种更优雅的方式来隐式地“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。

  1. function identify(){
  2. return this.name.toUpperCase();
  3. }
  4. function speak(){
  5. var greeting = "Hello, I'm " + identify.call(this);
  6. console.log(greeting);
  7. }
  8. var me = {
  9. name : "Kyle"
  10. };
  11. var you = {
  12. name : "Reader"
  13. };
  14. console.log(identify.call(me));//KYLE
  15. console.log(identify.call(you));//READER
  16. speak.call(me);//Hello, I'm KYLE
  17. speak.call(you);//Hello, I'm READER

如果不使用this,那就需要给identify()speak()显式传入一个上下文对象。

  1. function identify(context){
  2. return context.name.toUpperCase();
  3. }
  4. function speak(context){
  5. var greeting = "Hello, I'm " + identify(context);
  6. console.log(greeting);
  7. }
  8. var me = {
  9. name : "Kyle"
  10. };
  11. var you = {
  12. name : "Reader"
  13. };
  14. console.log(identify(you));//READER
  15. speak(me);//Hello, I'm KYLE

1.2 误解

人们很容易把this理解成指向函数自身。事实上,this并不是指向函数本身。

如果要从函数对象内部引用它自身,那只使用this是不够的。一般来说你需要通过一个指向函数对象的词法标识符(变量)来引用它。

  1. function foo(){
  2. foo.count = 4;//foo指向它自身
  3. }
  4. setTimeout(function(){
  5. //匿名函数无法指向自身
  6. },10);

在需要自引用时使用具名函数(表达式)。

  1. function foo(num){
  2. console.log("foo: " + num);
  3. this.count++;
  4. }
  5. foo.count = 0;
  6. var i;
  7. for(i=0; i<10; i++){
  8. if(i > 5){
  9. //使用call(..)可以确保this指向函数对象foo自身
  10. foo.call(foo,i);
  11. }
  12. }
  13. //foo: 6
  14. //foo: 7
  15. //foo: 8
  16. //foo: 9
  17. console.log(foo.count);//4

1.3 this到底是什么

this是 在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

2.this全面解析

2.1 调用位置

  1. function baz(){
  2. //当前调用栈是:baz
  3. //因此,当前调用位置是全局作用域
  4. console.log("baz");
  5. bar();//<--bar的调用位置
  6. }
  7. function bar(){
  8. //当前调用栈是:baz -> bar
  9. //因此,当前调用位置在baz中
  10. console.log("bar");
  11. foo();//<--foo的调用位置
  12. }
  13. function foo(){
  14. //当前调用栈是:baz -> bar -> foo
  15. //因此,当前调用位置在bar中
  16. console.log("foo");
  17. }
  18. baz();//<-- baz的调用位置

可以使用浏览器的开发者工具查看调用栈。

2.2 绑定规则

默认绑定

  1. function foo(){
  2. console.log(this.a);
  3. }
  4. var a = 2;
  5. foo();//2

this的默认绑定:this指向全局对象。

foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。

如果使用严格模式(strict模式),则不能将全局对象用于默认绑定,因此this会绑定到undefined。

通常来说你不应该在代码中混合实用strict模式和非strict模式。

隐式绑定

  1. function foo(){
  2. console.log(this.a);
  3. }
  4. var obj = {
  5. a : 2,
  6. foo : foo
  7. };
  8. obj.foo();//2

foo()被调用时,它的前面加上了对obj的引用。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因为调用foo()this被绑定到obj,因此this.aobj.a是一样的。

  1. function foo(){
  2. console.log(this.a);
  3. }
  4. var obj = {
  5. a : 2,
  6. foo : foo
  7. };
  8. var bar = obj.foo;
  9. var a = "oops, global";
  10. bar();//oops, global

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

显式绑定

在某个对象上强制调用函数,可以使用函数的call(...)apply(...)方法。

JavaScript提供的绝大多数函数以及你自己创建的所有函数都可以使用call(...)apply(...)方法。

  1. function foo(){
  2. console.log(this.a);
  3. }
  4. var obj = {
  5. a : 2
  6. };
  7. foo.call(obj);//2

通过foo.call(...),我们可以在调用foo时强制把它的this绑定到obj上。

如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(..)new Boolean(..)或者new Number(..))。这通常被称为“装箱”。

硬绑定
  1. function foo(){
  2. console.log(this.a);
  3. }
  4. var obj = {
  5. a : 2
  6. };
  7. var bar = function(){
  8. foo.call(obj);
  9. };
  10. bar();//2
  11. setTimeout(bar,100);//2
  12. //硬绑定的bar不可能再修改它的this
  13. bar.call(window);//2

硬绑定是一种非常常用的模式,ES5提供了内置的方法Function.prototype.bind

  1. function foo(something){
  2. console.log(this.a,something);
  3. return this.a + something;
  4. }
  5. var obj = {
  6. a : 2
  7. };
  8. var bar = foo.bind(obj);
  9. var b = bar(3);//2 3
  10. console.log(b);//5

new 绑定

在JavaScript中,构造函数只是一些使用new操作符时被调用的函数,它们并不会属于某个类,也不会实例化一个类。

包括内置对象函数在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行[[Prototye]]连接。
  3. 这个新对象会绑定到函数调用的this
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
  1. function foo(a){
  2. this.a = a;
  3. }
  4. var bar = new foo(2);
  5. console.log(bar.a);//2

2.3 优先级

默认绑定的优先级是四条规则中最低的。

显式绑定的优先级比隐式绑定的优先级高。

new绑定比隐式绑定优先级高。

硬绑定(显式绑定的一种)比new绑定的优先级高。

我们可以根据优先级来判断函数在某个调用位置应用的是哪条规则:

  1. 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
  2. 函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
  3. 函数在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
  4. 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。

2.4 绑定例外

如果你把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的时候默认绑定规则。

  1. function foo(){
  2. console.log(this.a);
  3. }
  4. var a = 2;
  5. foo.call(null);//2

总是使用null来忽略this绑定可能产生一些副作用。

  1. function foo(){
  2. console.log(this.a);
  3. }
  4. var a = 2;
  5. var o = {a:3, foo:foo};
  6. var p = {a:4};
  7. o.foo();//3
  8. (p.foo = o.foo)();//2

赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo(),所以这里会应用默认绑定。

JS的关键字this的更多相关文章

  1. js 中关键字 this的用法

    <1>  js中this 的用法?  (key:函数是由调用的,四种情况标红可知) (http://www.ruanyifeng.com/blog/2010/04/using_this_k ...

  2. js基础 1.简单js 语法 关键字 保留字 变量

    简单js JavaScript 是一个松散性的语言 对象属性却不想c中的结构体或者c++ 和java的对象, 对象继承机制 使用原型的prototype(原型链),js的分为三部分ECMAScript ...

  3. day1——js方法关键字的问题(onclick点了没反应)

    <a href="javascript:void(0);"   onclick="search();" >提交</a> js代码: fu ...

  4. js常用关键字和函数

    document.createElement("div"): 创建一个div元素申明一个变量 document.body.appendChild(div);   将创建好的div添 ...

  5. js 查找关键字

    查找:4种: 1. 查找固定关键字,仅返回位置,可指定开始位置: var i=str.indexOf("kword"[,starti]); str.lastIndexOf(&quo ...

  6. JavaScript高级程序设计(五): js的关键字instanceof和typeof使用

    JavaScript中instanceof和typeof 常用来判断一个变量是否为空,或者是什么类型的.但它们之间还是有区别的: 一.typeof 1.含义:typeof返回一个表达式的数据类型的字符 ...

  7. ASP.NET给前端动态添加修改 CSS样式JS 标题 关键字

    有很多网站读者能换自己喜欢的样式,还有一些网站想多站点共享后端代码而只动前段样式,可以采用动态替换CSS样式和JS. 如果是webform 开发,可以用下列方法: 流程是首先从数据中或者xml读取数据 ...

  8. [js]this关键字代表当前执行的主体

    点前是谁,this就是谁 <div id="div1" class="div1"></div> <div id="div ...

  9. js new关键字

    实现new 关键字只需4步 1. 声明一个对象: 2. 把这个对象的__proto__ 指向构造函数的 prototype; 3. 以构造函数为上下文执行这个对象: 4. 返回这个对象. 简洁的代码示 ...

随机推荐

  1. 企业上云这四大要点,你 get 了吗?

    本文由 Platform9(一家专注于云计算.专有云.混合云.OpenStack 以及容器技术的北美初创公司)技术产品营销经理 Akshai Parthasarathy 撰写,描述了企业在向云基础设施 ...

  2. Kubernetes集群部署篇( 一)

    K8S集群部署有几种方式:kubeadm.minikube和二进制包.前两者属于自动部署,简化部署操作,我们这里强烈推荐初学者使用二进制包部署,因为自动部署屏蔽了很多细节,使得对各个模块感知很少,非常 ...

  3. Java中的==符号与equals()的使用(测试两个变量是否相等)

    Java 程序中测试两个变量是否相等有两种方式:一种是利用 == 运算符,另一种是利用equals()方法. 当使用 == 来判断两个变量是否相等时,如果两个变量是基本类型变量,且都是数值类型(不一定 ...

  4. 关于React面试题汇总

    1.redux中间件 中间件提供第三方插件的模式,自定义拦截 action -> reducer 的过程.变为 action -> middlewares -> reducer .这 ...

  5. [salt] jinja模板中变量使用pillar的几种方法

    先转载下jinja模板中使用变量的方法,后文主要讲解pillar的变量使用方法 一.jinja模版的使用方法: 1.file状态使用template参数 - template:jinja 2.模版文件 ...

  6. OO第四阶段总结

    一.测试与正确性论证的区别 从哲学的角度来说,正确性论证与测试的关系就像理论与实践的关系一样. 使用测试的方法检验程序正确性确实是一个非常方便可行且广泛运用的方法.可以通过几个简单或复杂的测试样例,迅 ...

  7. 文件上传到tomcat服务器 commons-fileupload的详细介绍与使用

    三个类:DiskFileUpload.FileItem和FileUploadException.这三个类全部位于org.apache.commons.fileupload包中. 首先需要说明一下for ...

  8. 45度炸队Alpha冲刺博客集

    博客集如下: Alpha冲刺Day1:第一天冲刺记录 Alpha冲刺Day2:第二天冲刺记录 Alpha冲刺Day3:第三天冲刺记录 Alpha冲刺Day4:第四天冲刺记录 Alpha冲刺Day5:第 ...

  9. validating & update ctabfolder css

    总是查错 结果把validating全部都反选,然后老是update ctabfolder css update ctabfolder css has encountered a problem An ...

  10. Oracle 11g R2 for Win7旗舰版(64位)- 安装

    1.下载Oracle 11g R2 for Windows的版本                                   下载地址:http://www.oracle.com/techne ...