this 指向在js中会被以下三种属性改变:
1:定时器;
2:new 出来的构造函数;
3:call 和 apply;
例子1:
function a(){ var user = "追梦子"; console.log(this.user); //undefined console.log(this); //Window } a();
按照我们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就可以证明。
function a(){ var user = "追梦子"; console.log(this.user); //undefined console.log(this); //Window } window.a();
和上面代码一样吧,其实alert也是window的一个属性,也是window点出来的。
例子2:
var o = { user:"追梦子", fn:function(){ console.log(this.user); //追梦子 } } o.fn();
这里的this指向的是对象o,因为你调用这个fn是通过o.fn()执行的,那自然指向就是对象o,这里再次强调一点,this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,一定要搞清楚这个。
其实例子1和例子2说的并不够准确,下面这个例子就可以推翻上面的理论。
如果要彻底的搞懂this必须看接下来的几个例子
本文出处:追梦子博客
例子3:
var o = { user:"追梦子", fn:function(){ console.log(this.user); //追梦子 } } window.o.fn();
这段代码和上面的那段代码几乎是一样的,但是这里的this为什么不是指向window,如果按照上面的理论,最终this指向的是调用它的对象,这里先说个而外话,window是js中的全局对象,我们创建的变量实际上是给window添加属性,所以这里可以用window点o对象。
这里先不解释为什么上面的那段代码this为什么没有指向window,我们再来看一段代码。
var o = { a:10, b:{ a:12, fn:function(){ console.log(this.a); //12 } } } o.b.fn();
这里同样也是对象o点出来的,但是同样this并没有执行它,那你肯定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解this的指向的问题。
情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,例子3可以证明,如果不相信,那么接下来我们继续看几个例子。
var o = { a:10, b:{ // a:12, fn:function(){ console.log(this.a); //undefined } } } o.b.fn();
尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西。
还有一种比较特殊的情况,例子4:
var o = { a:10, b:{ a:12, fn:function(){ console.log(this.a); //undefined console.log(this); //window } } } var j = o.b.fn; j();
这里this指向的是window,是不是有些蒙了?其实是因为你没有理解一句话,这句话同样至关重要。
this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子3是不一样的,例子3是直接执行了fn。
this讲来讲去其实就是那么一回事,只不过在不同的情况下指向的会有些不同,上面的总结每个地方都有些小错误,也不能说是错误,而是在不同环境下情况就会有不同,所以我也没有办法一次解释清楚,只能你慢慢地的去体会。
构造函数版this:
function Fn(){ this.user = "追梦子"; } var a = new Fn(); console.log(a.user); //追梦子
这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,将这个this指向对象a,为什么我说a是对象,因为用了new关键字就是创建一个对象实例,理解这句话可以想想我们的例子3,我们这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。
除了上面的这些以外,我们还可以自行改变this的指向,关于自行改变this的指向请看JavaScript中call,apply,bind方法的总结这篇文章,详细的说明了我们如何手动更改this的指向。
更新一个小问题当this碰到return时
function fn() { this.user = '追梦子'; return {}; } var a = new fn; console.log(a.user); //undefined
再看一个
function fn() { this.user = '追梦子'; return function(){}; } var a = new fn; console.log(a.user); //undefined
再来
function fn() { this.user = '追梦子'; return 1; } var a = new fn; console.log(a.user); //追梦子
function fn() { this.user = '追梦子'; return undefined; } var a = new fn; console.log(a.user); //追梦子
什么意思呢?
如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
function fn() { this.user = '追梦子'; return undefined; } var a = new fn; console.log(a); //fn {user: "追梦子"}
还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。
function fn() { this.user = '追梦子'; return null; } var a = new fn; console.log(a.user); //追梦子
知识点补充:
1.在严格版中的默认的this不再是window,而是undefined。
2.new操作符会改变函数this的指向问题,虽然我们上面讲解过了,但是并没有深入的讨论这个问题,网上也很少说,所以在这里有必要说一下。
function fn(){ this.num = 1; } var a = new fn(); console.log(a.num); //1
为什么this会指向a?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。
2017-09-15 11:49:14
注意: 当你new一个空对象的时候,js内部的实现并不一定是用的apply方法来改变this指向的,这里我只是打个比方而已.
if (this === 动态的\可改变的) return true;
引用类型---函数
在ECMAScript中,Function(函数)类型实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针。
- 1
- 2
一.函数的声明方式
1.普通的函数声明
function sum(num1,num2){
return num1+num2;
}
- 1
- 2
- 3
2.使用变量初始化函数
var sum = function(num1,num2){
return num1+num2;
}
- 1
- 2
- 3
3.使用Function构造函数
var box= new Function('num1', 'num2' ,'return num1 + num2');
PS:第三种方式我们不推荐,因为这种语法会导致解析两次代码(第一次解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。但我们可以通过这种语法来理解"函数是对象,函数名是指针"的概念。
函数名仅仅是指向函数的指针,因此函数名和包含对象指针的其他变量一样。
也就是说,不同的函数名可以指向同一个函数。
- 1
- 2
- 3
- 4
- 5
- 6
function hcd(a,b){
return a+b;
}
alert(hcd(1,2)); //3
var h = hcd; //注意:不带括号的函数名表示的是访问函数指针,不是调用函数
alert(h(3,2)); //5,因为复制的是指针,其实指向的是同一个函数
hcd = null; //表示hcd就和函数断绝关系了,他就不是函数指针了
alert(hcd(0,1)) //hcd is not a function
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
首先定义了一个函数hcd,将h设置为与hcd相等,(注意:使用不带圆括号的函数名是访问函数指针,而非调用函数结果),这时hcd和h就指向同一个函数。
因为函数的根源是对象,所以将hcd设置为空对象null,就可以将hcd和函数“断绝关系”。
但是这并不影响h指向函数。
- 1
- 2
- 3
- 4
二.函数的特点
1. 没有重载
function h(a){
return a+1;
}
function h(b){
return b+2;
}
alert(h(2));//4
其实和这个是一样的:
var h = function(a){
return a+1;
}
h = function(b){
return b+2;
}
alert(h(2));//4
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
2. 函数声明和函数表达式
函数声明:
解析器会先读取函数声明,并使其在执行任何代码前可用。
alert(hcd(1,2));//3
function hcd(a,b){
return a+b;
}
只是因为在解析时将函数声明放在了代码的顶部
函数表达式:
是按代码的顺序执行的。
alert(hcd(1,2));
var hcd = function(a,b){
return a+b;
}
这是会报错误hcd is not a function;
除了这点差别外,其他是一样的。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
3.作为值的函数
var hcd = function(a){
return a+5;
}
function h(b,c){
return b(c);
}
alert(h(hcd,10));//15
h函数接受两个参数,第一个参数为函数,第二个参数为传递给改函数的一个值。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
一个数组,含有多个对象,根据对象的某一属性值的大小,对对象进行排序
var data =[
{
name:"a",
age:10
},
{
name:"b",
age:11
},
{
name:"v",
age:20
},
{
name:"g",
age:8
}
]
function compare(s){
return function hcd(obj1,obj2){
var value1 = obj1[s];
var value2 = obj2[s];
if(value1<value2){
return -1;
}else if(value1>value2){
return 1;
}else{
return 0;
}
}
}
data.sort(compare("age"))
console.log(data[0].age)//8
console.log(data[1].age)//10
console.log(data[2].age)//11
console.log(data[3].age)//20
- 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
- 38
4.函数内部的属性
在函数的内部有两个特殊的对象:arguments和this
- 1
- 2
arguments
arguments:类数组对象,包含所有传入函数中的参数。argument的callee属性指向该函数。
- 1
- 2
例如:
常见的递归函数
function hcd(a){
if(a<=1){
return 1;
}else{
return a*hcd(a-1)
}
}
console.log(hcd(4))//24
这样的话就会将执行函数紧紧的与hcd耦合在一起。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
下面的方法会更好。
function hcd(a){
if(a<=1){
return 1;
}else{
return a*arguments.callee(a-1)
}
}
console.log(hcd(4))//24
这样一来:
var text = hcd;
hcd = function(){
return 0;
}
console.log(text(4))//24
console.log(hcd(24))//0
变量text获取了hcd的值,其实是在两外一个位置上保存了函数的指针。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
this:
this引用的是当前函数执行的环境对象。
- 1
- 2
wndow.color = "red";
var o = {
color:"blue"
};
function say(){
console.log(this.color)
}
say(); //"red"
o.say = say;
o.say(); //"blue"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
say函数为在全局定义的函数,在调用函数前,并不能确定this是那个对象。
当在全局中调用时,this引用的是全局对象window,换句话说this.color会转换为window.color。
而当将say函数赋值给o.say()时,this引用的是对象o。
- 1
- 2
- 3
- 4
5.函数的属性和方法
函数就是对象,所以有属性和方法。
每个函数都有两个属性:length,prototype
- 1
- 2
- 3
length
length:接受参数的个数
- 1
- 2
function hcd(a,b){
return a+b;
}
function h(a){
return a;
}
alert(hcd.length); //2
alert(h.length); //1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
prototype
对于prototype属性,它是保存所有实例方法的真正所在,也就是原型。这个属性,我们将在面向对象一章详细介绍。而prototype下有两个方法:apply()和call()每个函数都有2个非继承而来的方法call和apply,这两个方法可以让函数在特定的作用域中调用函数,其实等于设置this。
- 1
- 2
apply
apply:参数1.运行函数的作用域,参数2.数组,也可以是数组实例,也可以是arguments
- 1
- 2
function hcd(a,b){
return a+b;
}
function h1(c,d){
return hcd.apply(this,arguments);
}
function h2(c,d){
return hcd.apply(this,[c,d]);
}
alert(h1(10,10));
alert(h2(10,10));
在全局环境下调用h2和h1,所以传入hcd的this即为window对象
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
call:
和apply的用法类似,只不过参数2不为数组
- 1
- 2
function h2(c,d){
return hcd.call(this,c,d);
}
apply和call的真正强大在于扩充函数赖以运行的作用域。
window.color = "red";
var o = {
color:"blue"
};
function hcd(){
alert(this.color)
}
//在全局环境下运行
hcd(); //"red"
//在函数调用的环境下运行
hcd.call(this); //"red"
//在全局环境下运行
hcd.call(window); //"red"
//在o环境下运行
hcd.call(o); //"blue"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
bind()
创建一个函数的实例,this会绑定到传给bind()函数的值。
- 1
- 2
window.color = "red";
var o = {
color:"blue"
};
function hcd(){
alert(this.color)
}
var objectSay = hcd.bind(o);
objectSay()//"blue"
hcd()调用bind()并传入对象o,意思就说在环境变量为o的情况下,创建一个函数实例objectSay。