前言
最近在学习javacript的过程中,看到的一些题目。考到了很多javascript中的知识点。在这里总结一下:
CODE
var a=10;
function test(){
a=5;
alert(a);
alert(this.a);
var a;
alert(this.a);
alert(a);
}
test();
new test();
test和new test分别输出什么?
答案是5 10 10 5 和 5 undefined undefined 5
这题中涵盖的知识点有:函数与变量的提前声明;this的用法;new的含义。
函数与变量提前声明:
Javascript的解析器会将当前作用域内声明的所有变量和函数放到作用域的开始处,但是,只有变量的声明被提到作用域的开始处了,而赋值的操作被保留在了原地!
所以题中的函数内的实际顺序应该是:var a;a=5;alert(a);alert(this.a);alert(a);
那么函数的提前声明呢?
Javascript的解析器允许在函数声明之前使用,也就是说,函数声明不仅仅是函数名被提前了,整个函数的定义也被提前了,下面是个简单的例子:
test();
function test(){
alert("You can use the function first");
}
还有一种情况是,函数赋值给变量,下面看另外一个例子:
test1();
alert(test2);
function test1(){
alert("I'm test1!");
}
var test2=function(){
alert("I'm test2!")
};
结果是第一个弹出了I’m test1!第二个确是undefined。这里就很明显了,与之前变量声明的提前类似,变量被提前了,而赋值却没有提前,所以提示undefined。
this的用法
在javascript中,this的调用模式一共有四种:
1.方法调用模式:
当一个函数被保存为对象的一个属性时,我们称他为一个方法。而当一个方法被调用时,this被绑定到该对象。
例如:
var myObject = {
value :0;
increment:fucntion (inc){
this.value += typeof inc ==='number' ? inc:1;//这行解释一下,这里用到了一个三目运算符:
若typeof inc==='number'为true则this.value=this.value+inc;若typeof inc==='number'为false,则this.value=this.value+1
}
};
myObject.increment();
document.writeln(myObject.value); //1
myObject.increment(2);
document.writeln(myObject.value); //3
方法可以试用this去访问对象,所以它可以从对象中取的或者修改对象。this到对象的绑定发生在它被调用的时候。这种“超级”迟绑定使得函数可以对this高度复用。通过this可取得它们所属对象上下文的方法称为公共方法。
- 函数调用模式:
当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用的:
var sum=add(3,4);//sum值为7;
以此模式调用函数时,this被绑定到全局对象。这是语言设计上的一个错误。
倘若语言设计正确,那么当内部函数调用时,this应该仍然绑定到外部函数的this变量。这个错误设计的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。幸运的是,有一个很容易的解决方案:
如果该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this。按照约定,我把那个变量命名为that。
例子如下:
myObject.double=function(){
var that=this;
var helper=function(){
that.value=add(that.value,that.value);
};
helper();
};
myObject.double();//以方法的形式调用double。
一句话总结:函数调用模式下的this指向的全局变量window。
- 构造器调用模式
一个函数,如果创建的目的就是希望结合new前缀来调用,那它就被称为构造器函数。
按照约定,它们保存在以大写格式命名的变量里。如果调用构造器函数时没有在前面加上new,可能会发生非常糟糕的事情,既没有编译时警告,也没有运行时警告,所以大写约定非常重要。
如果在一个函数面前带上new来调用,那么背地里将会创建一个连接到该函数的prototype成员的新对象(新对象为构造函数的一个实例),同时this会被绑定到那个新对象上。
var Quo=function(string){
this.status=string;
};
Quo.prototype.get_status=function(){
return this.status;
};
var myQuo=new Quo('confused');
console.log(myQuo.get_staus());//打印‘confused’
一句话总结:在用new的情况下调用函数,this被绑定到那个新对象上。
- Apply调用模式
因为JavaScript是一门函数式的面向对象编程语言,所以函数可以拥有方法。
apply方法让我们构建一个参数数组传递给调用函数。它允许我们选择this的值。apply方法接收两个参数,第1个是要绑定给this的值,第2个就是一个参数数组。
call方法与apply类似,将apply第二参数拆开为单个的参数。
例子如下:
//例1
function add(a,b){
this.value=a+b;
};
var obj={
value:0
};
add.call(obj,1,2);
add.apply(obj,[1,2])
cosole.log(obj.value);//call和apply将this绑定给了obj,所以this.value=0,相加后为3
//例2
var array=[3,4];
var sum=add.apply(null,array);//sum值为7
var statusObj={
status: 'A-OK'
};
//由于status没有继承自Quo.prototype,但我们可以在statusObj上调用get_status方法,尽管statusObj并没有名为get_status的方法
var status=Quo.prototype.get_status.apply(statusObj);//status值为‘A-OK’(例2承自第二种模式的举例)
一句话总结,用call和apply时,this绑定到自己指定的对象。
new的含义
在题目中,加了new和不加new的结果是不同的,那么这个new究竟做了什么事情呢?
在javascript中,使用new关键字后,做了如下四件事情:
1.创建一个新的对象,它的对象类型是object.
2.设置这个新的对象的内部、可访问性、和[prototype]属性为构造函数(指prototype.constructor所指向的函数)中设置的;
3.执行构造函数,当this关键字被提及时,使用新创建对象的属性;
4.返回新创建对象的对象(除非构造方法中返回的是’无原型’)。
- :在创建新对象成功之后,如果调用一个新对象没有属性的时候,javascript会延原型链向上逐层查找对应内容。这类似与传统的’类继承’。
举例:
objMaker=function(){this.a='first';};
objMaker.prototype.b='second';
obj1=new objMaker();
obj1.a和obj1.b分别是什么呢?我们来分析一下:
首先使用new关键字后,发生了如下事情:
创建了一个叫obj1的空对象,obj1与{}一致;
obj1的[prototype]属性被设置为objMaker的原型属性的拷贝;
objMaker方法被执行,所以obj1.a倍设置为first。
obj.a;返回’first’。
而obj1.b呢?它没有b属性。所以javascript在obj1的[prototype]中去查找。它的[prototype]属性和objMaker.prototype属性一致。而objMaker.prototype属性有一个叫’b’的属性,值为’second’,所以返回’second’。
总结
现在让我们再次看看最初的题目:
var a=10;
function test(){a=5;alert(a);alert(this.a);var a;alert(this.a);alert(a); }
test();
new test();
首先构造了一个函数,这个函数是直接作为函数在后面被调用,属于上面提到的函数调用模式,所以其中的this指向全局。那么this.a=10;函数内,var a被提前了,故test();结果自然是5 10 10 5;
而使用了new关键字后,创建了一个空的对象,其中的this并不指向全局了,所以this.a此时是undefined的状态。故结果是5 undefined undefined 5。
结语
这次一个总结了这几个知识点,都是JS中比较基础必须弄懂的地方,其中new的用法中牵扯到了原型链的问题。原型链和闭包是JS中不可忽视的重要知识点。下一篇先着重的来剖析一下JS原型链的概念~