JavaScript 中的 this/new、apply/call

javascript技术难点(三)之this、new、apply和call详解

2014/12/10 · JavaScript
· apply,
call,
Javascript,
奥门威尼斯网址 ,new,
this

原文出处:
夏天的森林   

讲解this指针的原理是个很复杂的问题,如果我们从javascript里this的实现机制来说明this,很多朋友可能会越来越糊涂,因此本篇打算换一个思路从应用的角度来讲解this指针,从这个角度理解this指针更加有现实意义。

下面我们看看在java语言里是如何使用this指针的,代码如下:

JavaScript

public class Person { private String name; private String sex; private
int age; private String job; public Person(String name, String sex, int
age, String job) { super(); this.name = name; this.sex = sex; this.age =
age; this.job = job; } private void showPerson(){
System.out.println(“姓名:” + this.name); System.out.println(“性别:” +
this.sex); System.out.println(“年龄:” + this.age);
System.out.println(“工作:” + this.job); } public void printInfo(){
this.showPerson(); } public static void main(String[] args) { Person
person = new Person(“马云”, “男”, 46, “董事长”); person.printInfo(); } }
//姓名:马云 //性别:男 //年龄:46 //工作:董事长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Person {
    
    private String name;
    private String sex;
    private int age;
    private String job;
 
    public Person(String name, String sex, int age, String job) {
        super();
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.job = job;
    }
 
    private void showPerson(){
        System.out.println("姓名:" + this.name);
        System.out.println("性别:" + this.sex);
        System.out.println("年龄:" + this.age);
        System.out.println("工作:" + this.job);
    }
 
    public void printInfo(){
        this.showPerson();
    }
    
    public static void main(String[] args) {
        Person person = new Person("马云", "男", 46, "董事长");
        person.printInfo();
    }
 
}
 
//姓名:马云
//性别:男
//年龄:46
//工作:董事长

上面的代码执行后没有任何问题,下面我修改下这个代码,加一个静态的方法,静态方法里使用this指针调用类里的属性,如下图所示:

奥门威尼斯网址 1

我们发现IDE会报出语法错误“Cannot use this in a static
context”,this指针在java语言里是不能使用在静态的上下文里的。

在面向对象编程里有两个重要的概念:一个是类,一个是实例化的对象,类是一个抽象的概念,用个形象的比喻表述的话,类就像一个模具,而实例化对象就是通过这个模具制造出来的产品,实例化对象才是我们需要的实实在在的东西,类和实例化对象有着很密切的关系,但是在使用上类的功能是绝对不能取代实例化对象,就像模具和模具制造的产品的关系,二者的用途是不相同的。

有上面代码我们可以看到,this指针在java语言里只能在实例化对象里使用,this指针等于这个被实例化好的对象,而this后面加上点操作符,点操作符后面的东西就是this所拥有的东西,例如:姓名,工作,手,脚等等。

其实javascript里的this指针逻辑上的概念也是实例化对象,这一点和java语言里的this指针是一致的,但是javascript里的this指针却比java里的this难以理解的多,究其根本原因我个人觉得有三个原因:

原因一:javascript是一个函数编程语言,怪就怪在它也有this指针,说明这个函数编程语言也是面向对象的语言,说的具体点,javascript里的函数是一个高阶函数,编程语言里的高阶函数是可以作为对象传递的,同时javascript里的函数还有可以作为构造函数,这个构造函数可以创建实例化对象,结果导致方法执行时候this指针的指向会不断发生变化,很难控制。

原因二:javascript里的全局作用域对this指针有很大的影响,由上面java的例子我们看到,this指针只有在使用new操作符后才会生效,但是javascript里的this在没有进行new操作也会生效,这时候this往往会指向全局对象window。

原因三:javascript里call和apply操作符可以随意改变this指向,这看起来很灵活,但是这种不合常理的做法破坏了我们理解this指针的本意,同时也让写代码时候很难理解this的真正指向

上面的三个原因都违反了传统this指针使用的方法,它们都拥有有别于传统this原理的理解思路,而在实际开发里三个原因又往往会交织在一起,这就更加让人迷惑不解了,今天我要为大家理清这个思路,其实javascript里的this指针有一套固有的逻辑,我们理解好这套逻辑就能准确的掌握好this指针的使用。

我们先看看下面的代码:

JavaScript

<script type=”text/javascript”> this.a = “aaa”;
console.log(a);//aaa console.log(this.a);//aaa
console.log(window.a);//aaa console.log(this);// window
console.log(window);// window console.log(this == window);// true
console.log(this === window);// true </script>

1
2
3
4
5
6
7
8
9
10
<script type="text/javascript">
    this.a = "aaa";
    console.log(a);//aaa
    console.log(this.a);//aaa
    console.log(window.a);//aaa
    console.log(this);// window
    console.log(window);// window
    console.log(this == window);// true
    console.log(this === window);// true
</script>

在script标签里我们可以直接使用this指针,this指针就是window对象,我们看到即使使用三等号它们也是相等的。全局作用域常常会干扰我们很好的理解javascript语言的特性,这种干扰的本质就是:

在javascript语言里全局作用域可以理解为window对象,记住window是对象而不是类,也就是说window是被实例化的对象,这个实例化的过程是在页面加载时候由javascript引擎完成的,整个页面里的要素都被浓缩到这个window对象,因为程序员无法通过编程语言来控制和操作这个实例化过程,所以开发时候我们就没有构建这个this指针的感觉,常常会忽视它,这就是干扰我们在代码里理解this指针指向window的情形。

干扰的本质还和function的使用有关,我们看看下面的代码:

JavaScript

<script type=”text/javascript”> function ftn01(){ console.log(“I
am ftn01!”); } var ftn02 = function(){ console.log(“I am ftn02!”); }
</script>

1
2
3
4
5
6
7
8
<script type="text/javascript">
    function ftn01(){
       console.log("I am ftn01!");
    }
    var ftn02 = function(){
        console.log("I am ftn02!");
    }
</script>

上面是我们经常使用的两种定义函数的方式,第一种定义函数的方式在javascript语言称作声明函数,第二种定义函数的方式叫做函数表达式,这两种方式我们通常认为是等价的,但是它们其实是有区别的,而这个区别常常会让我们混淆this指针的使用,我们再看看下面的代码:

JavaScript

<script type=”text/javascript”> console.log(ftn01);//ftn01()
注意:在firebug下这个打印结果是可以点击,点击后会显示函数的定义
console.log(ftn02);// undefined function ftn01(){ console.log(“I am
ftn01!”); } var ftn02 = function(){ console.log(“I am ftn02!”); }
</script>

1
2
3
4
5
6
7
8
9
10
<script type="text/javascript">
    console.log(ftn01);//ftn01()  注意:在firebug下这个打印结果是可以点击,点击后会显示函数的定义
    console.log(ftn02);// undefined
    function ftn01(){
       console.log("I am ftn01!");
    }
    var ftn02 = function(){
        console.log("I am ftn02!");
    }
</script>

这又是一段没有按顺序执行的代码,先看看ftn02,打印结果是undefined,undefined我在前文里讲到了,在内存的栈区已经有了变量的名称,但是没有栈区的变量值,同时堆区是没有具体的对象,这是javascript引擎在预处理(群里东方说预处理比预加载更准确,我同意他的说法,以后文章里我都写为预处理)扫描变量定义所致,但是ftn01的打印结果很令人意外,既然打印出完成的函数定义了,而且代码并没有按顺序执行,这只能说明一个问题:

在javascript语言通过声明函数方式定义函数,javascript引擎在预处理过程里就把函数定义和赋值操作都完成了,在这里我补充下javascript里预处理的特性,其实预处理是和执行环境相关,在上篇文章里我讲到执行环境有两大类:全局执行环境和局部执行环境,执行环境是通过上下文变量体现的,其实这个过程都是在函数执行前完成,预处理就是构造执行环境的另一个说法,总而言之预处理和构造执行环境的主要目的就是明确变量定义,分清变量的边界,但是在全局作用域构造或者说全局变量预处理时候对于声明函数有些不同,声明函数会将变量定义和赋值操作同时完成,因此我们看到上面代码的运行结果。由于声明函数都会在全局作用域构造时候完成,因此声明函数都是window对象的属性,这就说明为什么我们不管在哪里声明函数,声明函数最终都是属于window对象的原因了

关于函数表达式的写法还有秘密可以探寻,我们看下面的代码:

JavaScript

<script type=”text/javascript”> function ftn03(){ var ftn04 =
function(){ console.log(this);// window }; ftn04(); } ftn03();
</script>

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
    function ftn03(){
        var ftn04 = function(){
            console.log(this);// window
        };
        ftn04();
    }
    ftn03();
</script>

运行结果我们发现ftn04虽然在ftn03作用域下,但是执行它里面的this指针也是指向window,其实函数表达式的写法我们大多数更喜欢在函数内部写,因为声明函数里的this指向window这已经不是秘密,但是函数表达式的this指针指向window却是常常被我们所忽视,特别是当它被写在另一个函数内部时候更加如此。

其实在javascript语言里任何匿名函数都是属于window对象,它们也都是在全局作用域构造时候完成定义和赋值,但是匿名函数是没有名字的函数变量,但是在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象,所以上面代码执行时候ftn04的this也是指向window,因为javascript变量名称不管在那个作用域有效,堆区的存储的函数都是在全局执行环境时候就被固定下来了,变量的名字只是一个指代而已。

这下子坏了,this都指向window,那我们到底怎么才能改变它了?

在本文开头我说出了this的秘密,this都是指向实例化对象,前面讲到那么多情况this都指向window,就是因为这些时候只做了一次实例化操作,而这个实例化都是在实例化window对象,所以this都是指向window。我们要把this从window变成别的对象,就得要让function被实例化,那如何让javascript的function实例化呢?答案就是使用new操作符。我们看看下面的代码:

JavaScript

<script type=”text/javascript”> var obj = { name:”sharpxiajun”,
job:”Software”, show:function(){ console.log(“Name:” + this.name +
“;Job:” + this.job); console.log(this);// Object { name=”sharpxiajun”,
job=”Software”, show=function()} } }; var otherObj = new Object();
otherObj.name = “xtq”; otherObj.job = “good”; otherObj.show =
function(){ console.log(“Name:” + this.name + “;Job:” + this.job);
console.log(this);// Object { name=”xtq”, job=”good”, show=function()}
}; obj.show();//Name:sharpxiajun;Job:Software
otherObj.show();//Name:xtq;Job:good </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script type="text/javascript">
    var obj = {
        name:"sharpxiajun",
        job:"Software",
        show:function(){
            console.log("Name:" + this.name + ";Job:" + this.job);
            console.log(this);// Object { name="sharpxiajun", job="Software", show=function()}
        }
    };
    var otherObj = new Object();
    otherObj.name = "xtq";
    otherObj.job = "good";
    otherObj.show = function(){
        console.log("Name:" + this.name + ";Job:" + this.job);
        console.log(this);// Object { name="xtq", job="good", show=function()}
    };
    obj.show();//Name:sharpxiajun;Job:Software
    otherObj.show();//Name:xtq;Job:good
</script>

这是我上篇讲到的关于this使用的一个例子,写法一是我们大伙都爱写的一种写法,里面的this指针不是指向window的,而是指向Object的实例,firebug的显示让很多人疑惑,其实Object就是面向对象的类,大括号里就是实例对象了,即obj和otherObj。Javascript里通过字面量方式定义对象的方式是new
Object的简写,二者是等价的,目的是为了减少代码的书写量,可见即使不用new操作字面量定义法本质也是new操作符,所以通过new改变this指针的确是不过攻破的真理。

下面我使用javascript来重写本篇开头用java定义的类,代码如下:

JavaScript

<script type=”text/javascript”> function Person(name,sex,age,job){
this.name = name; this.sex = sex; this.age = age; this.job = job;
this.showPerson = function(){ console.log(“姓名:” + this.name);
console.log(“性别:” + this.sex); console.log(“年龄:” + this.age);
console.log(“工作:” + this.job); console.log(this);// Person {
name=”马云”, sex=”男”, age=46, 更多…} } } var person = new
Person(“马云”, “男”, 46, “董事长”); person.showPerson(); </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script type="text/javascript">
    function Person(name,sex,age,job){
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.job = job;
        this.showPerson = function(){
            console.log("姓名:" + this.name);
            console.log("性别:" + this.sex);
            console.log("年龄:" + this.age);
            console.log("工作:" + this.job);
            console.log(this);// Person { name="马云", sex="男", age=46, 更多…}
        }
    }
    var person = new Person("马云", "男", 46, "董事长");
    person.showPerson();
</script>

看this指针的打印,类变成了Person,这表明function
Person就是相当于在定义一个类,在javascript里function的意义实在太多,function既是函数又可以表示对象,function是函数时候还能当做构造函数,javascript的构造函数我常认为是把类和构造函数合二为一,当然在javascript语言规范里是没有类的概念,但是我这种理解可以作为构造函数和普通函数的一个区别,这样理解起来会更加容易些

下面我贴出在《javascript高级编程》里对new操作符的解释:

new操作符会让构造函数产生如下变化:

1.       创建一个新对象;

2.       将构造函数的作用域赋给新对象(因此this就指向了这个新对象);

3.       执行构造函数中的代码(为这个新对象添加属性);

4.       返回新对象

关于第二点其实很容易让人迷惑,例如前面例子里的obj和otherObj,obj.show(),里面this指向obj,我以前文章讲到一个简单识别this方式就是看方法调用前的对象是哪个this就指向哪个,其实这个过程还可以这么理解,在全局执行环境里window就是上下文对象,那么在obj里局部作用域通过obj来代表了,这个window的理解是一致的。

第四点也要着重讲下,记住构造函数被new操作,要让new正常作用最好不能在构造函数里写return,没有return的构造函数都是按上面四点执行,有了return情况就复杂了,这个知识我会在讲prototype时候讲到。

Javascript还有一种方式可以改变this指针,这就是call方法和apply方法,call和apply方法的作用相同,就是参数不同,call和apply的第一个参数都是一样的,但是后面参数不同,apply第二个参数是个数组,call从第二个参数开始后面有许多参数。Call和apply的作用是什么,这个很重要,重点描述如下:

Call和apply是改变函数的作用域(有些书里叫做改变函数的上下文)

这个说明我们参见上面new操作符第二条:

将构造函数的作用域赋给新对象(因此this就指向了这个新对象);

Call和apply是将this指针指向方法的第一个参数。

我们看看下面的代码:

JavaScript

<script type=”text/javascript”> var name = “sharpxiajun”; function
ftn(name){ console.log(name); console.log(this.name); console.log(this);
} ftn(“101″); var obj = { name:”xtq” }; ftn.call(obj,”102″); /* *
结果如下所示: *101 T002.html (第 73 行) sharpxiajun T002.html (第 74
行) Window T002.html T002.html (第 75 行) T002.html (第 73 行) xtq
T002.html (第 74 行) Object { name=”xtq”} * */ </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script type="text/javascript">
    var name = "sharpxiajun";
    function ftn(name){
        console.log(name);
        console.log(this.name);
        console.log(this);
    }
    ftn("101");
    var obj = {
      name:"xtq"
    };
    ftn.call(obj,"102");
    /*
    * 结果如下所示:
    *101
     T002.html (第 73 行)
     sharpxiajun
     T002.html (第 74 行)
     Window T002.html
     T002.html (第 75 行)
     T002.html (第 73 行)
     xtq
     T002.html (第 74 行)
     Object { name="xtq"}
    * */
</script>

我们看到apply和call改变的是this的指向,这点在开发里很重要,开发里我们常常被this所迷惑,迷惑的根本原因我在上文讲到了,这里我讲讲表面的原因:

表面原因就是我们定义对象使用对象的字面表示法,字面表示法在简单的表示里我们很容易知道this指向对象本身,但是这个对象会有方法,方法的参数可能会是函数,而这个函数的定义里也可能会使用this指针,如果传入的函数没有被实例化过和被实例化过,this的指向是不同,有时我们还想在传入函数里通过this指向外部函数或者指向被定义对象本身,这些乱七八糟的情况使用交织在一起导致this变得很复杂,结果就变得糊里糊涂。

其实理清上面情况也是有迹可循的,就以定义对象里的方法里传入函数为例:

情形一:传入的参数是函数的别名,那么函数的this就是指向window;

情形二:传入的参数是被new过的构造函数,那么this就是指向实例化的对象本身;

情形三:如果我们想把被传入的函数对象里this的指针指向外部字面量定义的对象,那么我们就是用apply和call

我们可以通过代码看出我的结论,代码如下:

JavaScript

<script type=”text/javascript”> var name = “I am window”; var obj
= { name:”sharpxiajun”, job:”Software”, ftn01:function(obj){ obj.show();
}, ftn02:function(ftn){ ftn(); }, ftn03:function(ftn){ ftn.call(this); }
}; function Person(name){ this.name = name; this.show = function(){
console.log(“姓名:” + this.name); console.log(this); } } var p = new
Person(“Person”); obj.ftn01(p); obj.ftn02(function(){
console.log(this.name); console.log(this); }); obj.ftn03(function(){
console.log(this.name); console.log(this); }); </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script type="text/javascript">
var name = "I am window";
var obj = {
    name:"sharpxiajun",
    job:"Software",
    ftn01:function(obj){
        obj.show();
    },
    ftn02:function(ftn){
        ftn();
    },
    ftn03:function(ftn){
        ftn.call(this);
    }
};
function Person(name){
    this.name = name;
    this.show = function(){
        console.log("姓名:" + this.name);
        console.log(this);
    }
}
var p = new Person("Person");
obj.ftn01(p);
obj.ftn02(function(){
   console.log(this.name);
   console.log(this);
});
obj.ftn03(function(){
    console.log(this.name);
    console.log(this);
});
</script>

结果如下:

奥门威尼斯网址 2

最后再总结一下:

如果在javascript语言里没有通过new(包括对象字面量定义)、call和apply改变函数的this指针,函数的this指针都是指向window的

赞 8 收藏
评论

奥门威尼斯网址 3

0.关于this是指什么

  • 其实可以这么概括,this属于调用被调用的方法的主体,也就是,谁调用,谁就是this。
  • 虽然说起来这么简单,但是上面的话里面的概念其实涉及到:作为方法的调用(function)的this;作为构造函数里的this;作为call或者apply的this。
  • 以上三个概念,又涉及到js里的对象创建,和方法的继承,所以,要弄清楚this,就要弄清楚js里的对象创建和继承机制。

OS:Window 8.1

this

当使用在函数中时,this指代当前的对象,也就是调用了函数的对象。如果在一个对象上使用点或者方括号来访问属性或方法,这个对象就成了this。如果并没有使用“点”运算符调用某个对象,那么this将指向全局对象(global
object)。

1.作为方法调用(function)的this

这个是最为简单的,但也可以分为几种情况,我们写一个文件,叫functionThis.js
1.1 有如下代码:

function fnThis(){
    console.log(this);
}
fnThis();

打印结果如下:

奥门威尼斯网址 4

1-1

可以看到this当前是指向window,这个很好理解,因为this是指向调用者,而fnThis没有调用者,没有调用者就默认为window。

1.2 我们在文件继续添加代码,如下

var obj = {
    fnThis: function(){
        console.log(this);
    }
}
obj.fnThis();
var objIns = obj.fnThis;
objIns();

打印结果如下:

奥门威尼斯网址 5

1-2

可以看到,第1行的打印this是obj,第2行是window,会这样的差异是因为:obj.fnThis()的调用者是obj,所以this指向obj;而objIns()没有调用者,默认为window。

关键字:JavaScript,HTML,this,new,delete,call,apply。

new

它和this密切相关。它的作用是创建一个崭新的空对象,然后使用指向那个对象的this调用特定的函数。注意,含有this的特定函数不会返回任何值,只会修改this对象本身。new关键字将生成的this对象返回给调用方,而被new调用的函数成为构造函数。习惯的做法是将这些函数的首字母大写,这样用new调用他们的时候就容易识别了。

2.作为构造函数里的this

这里我们的demo文件为constructorThis.js

2.1
说到这里又要提一下构造函数,构造函数,就是可以构造一个对象的函数类,js里面最简单的莫过于直接定义一个对象,这个对象有一些属性的方法,然后用的时候直接拿来用,像我们functionThis.js这个文件里的就是这种情况,这是直接定义,另外还有一些其他的方式就先不展开讲了,下面的例子以比较常见的构造函数的形式为例,至于为什么要用构造函数,简单地讲就是可以用面向对象的形式去编程,可继承,等。

2.2 测试代码如下:

var thisObj;
var ConstructorThis = function(params){
    this.myParams = params;
    console.log(this);
    thisObj = this;
}
ConstructorThis.prototype.sayParams = function(){
    console.log(this.myParams);
}
var obj = new ConstructorThis('hi');
console.log(obj);
console.log(obj === thisObj)
obj.sayParams();

打印结果如下:

奥门威尼斯网址 6

2-2

我们可以看到第1行和第2行打印的对象从字面上看是一样的(字面一样不等于两个对象是同一个东西,后续可以自行初始化几个对象进行类似的对比验证),我们为了验证这个this是否就是obj,把this赋值给外部的thisObj并进行比较,结果是true,说明构造函数内的this确实就是new
构造函数() 后的对象。

2.3
如此一来,因为构造函数需要用new关键字实例化一个对象,那么和1-2作为方法的调用的情况相比,this似乎就不是指向被调用者了?

实际上:

var obj = new ConstructorThis('hi');

等价于:

var obj = {};
obj.__proto__ = ConstructorThis.prototype;
ConstructorThis.call(obj, 'hi');

将第一个代码块改为下面三行的代码块,我们再看一下打印结果:

奥门威尼斯网址 7

2-3

与2-2的情况是一致的。

也就是说,new 构造函数()
干了这么一件事情,第一步,创建一个空的对象;第二步,把空对象的原型指向构造函数的原型;第三步,再根据call传入参数(’hi’),同时把构造函数内部的this指向这个空对象,这样一来就完成了构造函数原型链的继承(第二步)和自身属性的赋值(第三步);最后返回新创建的对象。

构造函数内部this等于新创建的对象,关键就在第三步,用了call方法把创建的对象指向了内部的this,call和apply方法会在接下来讲。总之,构造函数内部的this指向用new创建的新对象。

 

apply

第一个参数被当作函数内部中的this使用,第二个参数是一个函数参数的数组。如,

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

var me = { firstName: "Kris", lastName: "Lee"};

Person.apply(me, ["John"]); // me是 {firstName: "John", lastName: undefined}

3.作为call或者apply的this

这里我们仍然创建一个demo文件callThis.js

3.1
call或者apply的作用就是改变某个方法的运行环境,也就是改变内部this关键字的指向,正因为有这样的作用,也在js的继承机制中其中有着显著的作用。正如2-3我们讲到的一样,构造函数用call把创建的一个类指向了内部的this关键字,因此构造函数类可以作为一个类被继承,每个不同的实例通过内部的this被赋予了不同的属性。

3.2 call和apply的用法其实很简单,我们看如下代码:

var CallThis = function(params){
    console.log(this)
    this.myParams = params;
}
CallThis.call();
var obj = {otherParams: 'ok'};
CallThis.call(obj, 'hi');
console.log(obj);

打印结果如下:

奥门威尼斯网址 8

3-2

第一次call没传任何参数,内部的this打印出来是window,这在第一部分的时候已经说明了;第二次call的时候第一个参数是一个对象,第二个参数对应构造函数的一个参数,可以看到this指向了obj,然后obj被添加了myParams属性,因此
构造函数.call(obj,
args)这种形式就是通过call方法把this指向obj,args是传入构造函数内部的参数,需要传多个参数是这样子的
构造函数.call(obj, args, args1, …,
argsN),第一个参数就是把this指向的对象,这和我们在2-3说到的,new的步骤分解,第三步是一致的。

apply和call的用法类似,构造函数.call(obj, args, args1, …, argsN)
等同于 构造函数.call(obj, [args, args1, …,
argsN]),我们修改一下代码,将

CallThis.call(obj, 'hi');

替换为

CallThis.apply(obj, ['hi']);

可见,打印结果一致:

奥门威尼斯网址 9

3-2-1

即,call和apply都能把上下文运行到具体的this环境里,不同的只是传参的形式,call参数的传递是分开传递,而apply是作为数组传递。

1.this

call

同apply相似,不同的是参数不以数组的方式作为call的第二个参数:

var me = { firstName: "Kris", lastName: "Lee"};
Person.call(me, "John", "Snow");  // me是 {firstName: "John", lastName: "Snow"}

4.总结

到这里就基本把this涉及到的情况说完了,包括作为方法的调用(function)的this;作为构造函数里的this;作为call或者apply的this;第二和第三种情况本质上是统一的。

这算是第一次写比较完整的文章,大部分是自己的理解,当然前期学习阶段也参考了一些资料,若有不当之处,还请指正,若有宝贵意见,也可以多多交流。

具体的实例代码和demo地址:

https://github.com/CHristopherkeith/front-end-summary-this-new

一般而言,在Javascript中,this指向函数执行时的当前对象。

bind

var a = function (arg1, arg2) {
  // function content
};

var b = a.bind(this, 12);
// equals
var b = function () {
  var arguments = [12]
}

2.new

在JavaScript中,使用new关键字后,意味着做了如下四件事情:

  • 创建一个新的对象,这个对象的类型是object
  • 设置这个新的对象的内部、可访问性和[[prototype]]属性为构造函数(指prototype.constructor所指向的构造函数)中设置的;
  • 执行构造函数,当this关键字被提及的时候,使用新创建的对象的属性;
  • 返回新创建的对象(除非构造方法中返回的是‘无原型’)。

3.delete

 delete操作符通常用来删除对象的属性,而不是一般的变量或者是函数。

4.call

应用于:Function 对象
调用一个对象的一个方法,以另一个对象替换当前对象。
call([thisObj[,arg1[, arg2[,   [,.argN]]]]])
参数:
thisObj 
可选项。将被用作当前对象的对象。 
arg1, arg2, , argN 
可选项。将被传递方法参数序列。 
说明:
call 方法可以用来代替另一个对象调用一个方法。call
方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj
指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作
thisObj。

5.apply

call,
apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例,也就是每个方法都有call,
apply属性.既然作为方法的属性,那它们的使用就当然是针对方法的了.这两个方法是容易混淆的,因为它们的作用一样,只是使用方式不同.

Function.apply(obj,args)方法能接收两个参数
obj:这个对象将代替Function类里this对象
args:这个是数组,它将作为参数传给Function(args–>arguments)

6.运行调试一下JS代码,有助于对以上概念的理解。

<html>
<body>
    <script>
        function Base(){
            this.name = 'Base';
            this.age = 1;
            this.show = function(){
                alert('Name:' + this.name + '\n' + 'Age:' + this.age.toString());
            };
            this.reset = function(name, age){
                this.name = name;
                this.age = age;
            };

            alert(this);
        }

        function A(){
            this.name = 'A';
            this.age = 1;
        }

        Base();
        var base = new Base();

        var isDeleted = delete base;
        alert('delete base: ' + isDeleted);//delete无效
        base.show();
        isDeleted = delete base.name;
        alert('delete base.name: ' + isDeleted);//delete有效
        base.show();

        base = new Base();
        var a = new A();
        base.show.call(a);
        base.show.apply(a);
        base.reset.call(a, 'AA', 11);
        base.show.call(a);
        base.reset.apply(a, ['AAA', 111]);
        base.show.apply(a);
    </script>
</body>
</html>

 7.使用call来实现类的继承。代码如下,在类B的构造函数里面调用“Base.call(this);”,则B可以使用Base里面的方法和属性。

        function B(){
            Base.call(this);
            this.name = 'B';
            this.age = 2;
        }

        var b = new B();
        b.show();

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注