LI@NG

JS that=this?

本视频教程地址: 点此查看 在Javascript中,“this”关键词是一个函数的执行环境(context)。“this”是一个对象的引用,该对象则是函数执行时所在的对象(javascript中,所有的函数都绑定在一个对象上(其实 函数本身也是一个对象)),因此这个对象就是该函数的执行环境,我们可以使用javascript函数自己内置的三个方法(因为函数也是对象,所有有对应的方法)call(), apply() 和 bind() 来改变其所绑定的对象,即执行环境。

请大家先记住:

1, javascript中的所有函数实际上都是某对象的“方法”

比如:

// 我们在全局区域定义了一个方法(其实已经绑定在window对象下面了)
function someMethod(){ ... }
// 它其实是window对象的一个方法
window.someMethod();

2, 我们也可以通过函数来构造对象

大家都知道我们可以通过var a = {}; 字面量方式去快速构造一个对象,在javascript我们也可以通过函数来构造一个对象!使用的是new 操作符! 比如: var a = new Student();

OK,到此,我们知道了javascrip中所有的函数其实都是某个对象的一个方法,这些函数的执行环境即为该函数上一级的”对象”,只是我们使用“this”这个关键词来指向该父级对象,所以this的改变就意味着父级“对象”的改变!

好的看例子:

// 我们在全局定义一个函数,该函数的父级对象是什么?是window
function parent() {
this.m1 = function () {
console.log( this === window ); //true
}
}
parent()
m1() // 在全局环境下调用m1
var a = {
method1: function () {
console.log( this === window ); //false
console.log( this === a ); // true
}
};

a.method1();

我们再来看一个例子,我们还可以通过什么方式来构造对象呢?对!通过构造函数!

function Student(firstName, lastName) {
console.log(this);
this.firstName = firstName;
this.lastName = lastName;
console.log(this);
}

// 如果这里我们直接调用该函数,那么this将会指向window
// Student() // Window 对象

// 但是我们来构造一个新的对象.

var stu1 = new Student('san', 'zhang');
// 这里值得说一下,一遇到new操作符,首先生成一个空的对象{},并将函数中的this关键词指向生成的新对象,然后我们通过函数里面的代码进行“构造”这个对象
// 所以大家看到的输出显示一个空对象,而后则是一个包括firstName和lastName属性的对象
// Student {}
// Student {firstName: "san", lastName: "zhang"}

我们进一步看一个例子:

var firstName = 'san';
var lastName = 'zhang';

function Student() {
this.firstName = firstName;
this.lastName = lastName;
this.fullName = function () {
return this.firstName + ' ' + this.lastName;
}
}

Student()
console.log(this.fullName()) // 结果是什么呢?
console.log(fullName()) // 结果是什么呢?

结果都是 sanzhang, 我们在全局(window对象下)定义了两个变量 firstName, lastName,也在全局(window对象下)定义了一个window的一个方法(Student 函数), 然后我们直接调用该函数,此时函数内部this指向的全都是window,因为我们并没有改变该函数的外部对象,也就是没有改变该函数的执行环境。

再一个例子

function Student(name, age) {
this.name = name;
this.age = age;
console.log(this); //这里会是神马呢?
var setAge = function (age) {
this.age = age;
}
setAge(99);
this.sayHi = function () {
return this.name + ' is ' + this.age;
}
console.log(this); // 这里又会是神马呢?
}

var stu1 = new Student('liang', 23);
console.log(stu1.sayHi()) // liang is 23

//----------------
// 然后大家再试着输出一下
console.log(age); // WOW!!! 居然是99!!!
// 大家看一下输出,在stu1这个新对象中有没有setAge?
// 我们在用Student这个函数构造stu1时,并没有构造进setAge去!所以setAge父级对象依然是?对,window!

接下来说一下如何使用call, apply, bind方法改变执行环境

先来说一下call 和 apply:

function Student(name, age) {
this.name = name;
this.age = age;
this.sayHi = function (name, age) {
return (name || this.name) + ' is ' + (age || this.age);
}
}

var stu1 = new Student('liang', 23); // 这里创建了一个对象,也就是更改了Student的执行环境从window到stu1,也就是将里面所有this指向了创建的新对象stu1

var stu2 = new Student('juha', 32); // 同理

// 这里就是基本的对象访问其中的方法。
console.log(stu1.sayHi()) // liang is 23

// 我们这里是用call(context, [arguments]) 第一个参数是我们想要更改的执行环境,随后大家可以填入自定义参数。这里我们将执行环境更改到stu2
console.log(stu1.sayHi.call(stu2)) // juha is 32
console.log(stu1.sayHi.call(stu2, 'tom', 40)) // tom is 40

// 再来说一下apply()
// apply() 和 call() 功能一样,只不过将后面的传参是一个数组形式!

console.log(stu1.sayHi.apply(stu2, ['tom', 40] )) // tom is 40

再来说一下bind() 使用函数的bind方法,可以更改函数的执行环境,但同时又返回了一个具有新执行环境的“新的函数” 上例子!

function Student(name, age) {
this.name = name;
this.age = age;
this.sayHi = function (name, age) {
return (name || this.name) + ' is ' + (age || this.age);
}
}

var stu = new Student('liang', 23);
var stuContext = new Student('juha', 32);

var sayHifunc = stu.sayHi.bind(stuContext); // 只是返回的是Student下面的sayHi方法!!但是使用的是stuContext的执行环境
console.log(sayHifunc()) // juha is 32

//-----------我们还可以利用bind来定义默认的参数
var sayHifunc = stu.sayHi.bind(stuContext, 'lauri'); // 所以sayHi这个函数第一个参数默认为lauri
// 下面我们只需传入一个参数,因为上一行我们已经设置了一个默认参数了
console.log(sayHifunc(38)) // lauri is 38