JavaScript中级
在javascript的函数中,除了函数声明时定义的形参之外,每个函数还可以接收另一个隐藏的参数:this,又称this引用。
this引用是一种在js代码中随时都可以使用的只读变量.
this的值(即它的指向)取决于调用的方式。
在javascript中this指向大致有四种情况:
1 、无任何前缀的函数调用时,this指向顶层对象或者叫全局对象,浏览器里是window(nodejs里是global)。
function fn(){
console.log(this);
}
fn(); //打印结果为window{...}
2 、方法调用的时候,this指向方法所在的对象
var robot = {
name:"cup",
say:function(){
console.log(this.name)
}
};
robot.say(); //打印结果为'cup'
3 、构造函数里,this指向新生成的实例
function Robot(name){
this.name = name;
this.say = function(){
console.log(this.name)
}
}
var robot_1 = new Robot('bower');
robot_1.say() // 打印结果为'bower'
var robot_2 = new Robot('cup');
robot_2.say() // 打印结果为'cup'
4 、apply/call调用的时候,this指向apply/call方法中的第一个参数
var robot_1 = {name:'cup'}
var robot_2 = {name:'bower'}
function say(){
console.log(this.name)
}
say.call(robot_1) // 打印结果为'cup'
say.call(robot_2) // 打印结果为'bower'
apply/call是js语法中一种特殊用法,两者的用法十分相似。
函数的apply与call方法
所有的函数都默认包含apply和call这两种方法,调用函数的apply或call方法,就相当于调用该函数。而apply和call的功能是,通过传参的方式,强制函数内的this指定某一对象。this引用的会被指向apply/call的第一个参数。
var robot_1 ={ name:"cup", say:function(){
console.log(this.name)
}
};
var robot_2 ={ name:"bower" };
robot1.say() //打印结果为cup
robot_1.say.apply(robot_2) // 打印结果为bower
//通过apply调用robot_1.say方法。方法内的this引用引用了robot_2
robot_1.say.call(robot_2) // 打印结果为bower
//通过call调用robot_1.say方法。方法内的this引用引用了robot_2
apply与call之间的不同之处在于两者对其他参数的传递方式。
- 对于apply来说,剩余的参数将通过数组来传递
- call是直接按参数列表传递
function say(age, gender){console.log("My name is " + this.name + ",I'm a " + age + " years old " + gender + ".")}
say.apply({name:"cup"}, [12, "boy"])
//打印结果为 My name is cup,I'm a 12 years old boy.
// this.name = "cup", age = 12, gender = "boy" 作为第二个参数的数组中的元素都是函数say的参数,按顺序依次对应
say.call({name:"cup"}, 12, "boy")
//打印结果为 My name is cup,I'm a 12 years old boy.
// this.name = "cup", age = 12, gender = "boy" 从第二个参数起的参数都是函数say的参数,按顺序依次对应
在实际的编程过程中,我们有时会为了函数回调而使用apply或call调用。
关于方法和this引用的具体例子解析:
1 、方法内的this调用
//对象定义
var robot = {
name : "cup",
say : function() { console.log( "Hi, I'm " + this.name + "."); }
}
robot.say() // 打印结果为 Hi, I'm cup.
//对象robot是被调用的对象,say是方法。
上面的例子:首先是将对象赋给了变量robot。这个对象有两个属性。属性name的值为"cup",属性say的值是一个函数。该函数称作say方法。 被调用的方法say内的关键字this引用指向了被调用的对象robot。
2 、函数内的this调用
如果将含有this的对象方法取出来单独执行
var robot = {
name : "cup",
say : function() { console.log( "Hi, I'm " + this.name + "."); }
}
var fn = robot.say;
// 将robot.say引用的函数赋值给全局变量 fn.
fn() // 打印结果为 Hi, I'm .
// 执行函数(全局的方法),this引用了全局对象,由于全局对象没有name属性,所以没有取到值.
根据我们之前讲的,现在这个函数由于是无任何前缀的直接调用的,this应该是指向了全局对象,我们确认一下this是否指向了全局对象
var robot = {
name : "cup",
say : function() { console.log( "Hi, I'm " + this.name + "."); }
}
var fn = robot.say; //将robot.say引用的函数赋值给全局变量 fn.
//相当于给全局对象定义了一个属性fn,并赋予robot.say所指代的函数.
var name = "bower",
//相当于给全局对象定义了一个属性name,赋值为"bower",在浏览器里,上面这行代码等价于 window.name = "bower";
fn() // 打印结果为 Hi, I'm bower.
//执行函数fn(相当于调用全局对象的fn方法),执行时this引用了全局对象.所以this.name的值是"bower".
一个典型的定义类的方法
function Car(x,y){
this.x = x;
this.y = y;
this.run = function(x_increase, y_increase){
this.x += x_increase;
this.y += y_increase;
}
this.show = function(){
console.log("( " + this.x + "," + this.y + " )");
}
}
通过上述代码,我们就拥有了一个名为Car的“类”。它有两个成员变量x和y还有一个成员函数run。那么我们就可以像用Object一样使用它:
var a_car = new Car(2,4); //获得了一个坐标为(2,4)的Car对象,x为2,y为9
a_car.run(10,5); //a_car.x变成了12,a_car.y变成了9
a_car.show(); //打印结果为 ( 12,9 )
当我们使用new Car(2,4)的时候,就会生成一个对象,在调用的过程中会调用所谓的构造函数。而在本页第一段代码中的Car函数就是一个构造函数。
它既是一个类,又是一个构造函数, 严格的讲,它应该是相当于一个类。
使用这种方式去自定义类,所有的成员定义都放在构造函数里,不论函数还是属性。这样做的话,每次构造一个对象,都要对所有的函数重新分配内存。 比如
var a_car = new Car(2,4);
var b_car = new Car(3,3);
上面的代码new了两次,相应的run和show被分配了两次内存。