this 的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。当你在代码中使用了 this,这个 this 的值就直接从执行的上下文中获取了,而不会从作用域链中搜寻
1:全局的this
指向window
- 在全局情况下
this
永远指向window
;
console.log(this===window)//true
- 普通函数调用的时候
this
也是指向window
(注意严格模式下为undefined
)
var x = 10; //window.x
function foo(){
console.log(this); //window
console.log(this.x); //10
}
foo(); //foo.call(window),window.foo()
2:对象方法
- 如果函数作为对象的方法来调用的时候,
this
指向调用它的该对象
注意这里声明用的不是let
,所以obj
没有自己的块级作用域;
var obj = {
x: 10,
foo: function () {
console.log(this); //{x: 10, foo: ƒ}
console.log(this.x); //10
}
};
obj.foo();
- 如果在对象方法中定义函数,那么也就是闭包,
this
是会指向window
var obj = {
x: 10,
foo: function () {
console.log(this) //{x: 10, foo: ƒ}
function f(){
console.log(this); //Window
console.log(this.x); //10
console.log(obj.x===window.x) //true,obj===window.obj
}f();
}
}
obj.foo();
函数虽然是在obj.foo
中定义的,但它仍然只是个普通函数。作用域的特性,自己内部没有就会向父函数里找,父函数没有,就会向更上级找,直到最终找到或找不到为止。
如果foo
方法不作为对象被调用,那么就指向调用它的那个
var obj = {
x: 10,
foo: function () {
console.log(this); //Window
console.log(this.x); //10
}
};
var fn = obj.foo; //window.fn
fn();
3:构造函数
构造函数就是由一个函数 new
出来的对象,一般构造函数的函数名首字母大写,例如像 Object
,Function
,Array
这些都属于构造函数
- 如果函数作为构造函数使用,那么其中的
this
就代表它即将new
出来的对象。
function Foo(){
this.x = 10;
console.log(this); //Foo {x:10}
}
var foo = new Foo();
console.log(foo.x); //10
- 但是如果直接调用
Foo
函数,而不是new Foo()
,这时候Foo()
就变成普通函数。
function Foo(){
this.x = 10;
console.log(this); //Window
}
var foo = Foo();
console.log(foo.x);//Uncaught TypeError: Cannot read property 'x' of undefined
4:构造函数的prototype
属性
构造函数的prototype
属性也就是原型对象
function Foo(){
this.x = 10;
this.xx=function(){
console.log(this) //Foo {x: 10, xx: ƒ}
}
}
Foo.prototype.getX = function () {
console.log(this); //Foo {x: 10, xx: ƒ}
console.log(this.x); //10
}
var foo = new Foo();
foo.getX();
在整个原型链中,this
代表的也是当前对象的值,指向构造函数。
5:函数用call,bind,apply
调用
var obj = {
x: 10
}
function foo(){
console.log(this); //{x: 10}
console.log(this.x); //10
}
//foo.call(obj)=obj.foo;f.foo.call(obj)=obj.foo。
//call和apply改变了函数的this上下文后便执行该函数, 而bind则是返回改变了上下文的一个函数
foo.call(obj); //call(obj,arg1,arg2)
foo.apply(obj); //apply(obj,[arg1,arg2])
foo.bind(obj)(); //还要再调用一下,多用于绑定回调函数
call,bind,apply
可以改变this
的指向,this
的值就取传入的对象的值。第一个参数如果为undefined,null
或空就相当于是window
。
6:箭头函数
函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象
var obj = {
x: 10,
foo: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10, foo: ƒ}
console.log(this.x); //10
}
}
}
fn()()();
}
}
obj.foo();
7:vue
的this
注意,不应该使用箭头函数来定义 method
函数 (例如 plus: () => this.a++
)。理由是箭头函数绑定了父级作用域的上下文,所以 this
将不会按照期望指向 Vue
实例,this.a
将是 undefined
。
为了弄清楚这个,我们先来弄懂这些:
我们在使用vue
的时候总会先new vue({})
,那么就是说vue
其实是一个构造函数。
function A(){
this.a=3;
this.aa={
aaa:function(){
console.log(this,'this') //指向aaa它自己这个函数
}
}
}
function A(){
this.a=3;
this.aa={
aaa:()=>{
console.log(this,'this')//箭头函数指向A这个函数,非箭头函数指向aaa这个函数
}
}
}
var b=new A()
b.aa.aaa()//箭头函数一开始就被定义指向了A,所以即使b是A的实例,this也不会指向b,而还是指向一开始生成被定义的A
function add(){ //正常不是箭头函数的
console.log(this===a) // true
}
var a = {};
a['add'] = function(args){
add.apply(a,[args]); //a.add();add的this就指向调用者a
};
a.add()
//有箭头函数的,用`bind,call,apply`无效
var bind=function(fn,vm){
return function(){
return fn.apply(vm,arguments)
}
}
var a={'_a':3};
var add=()=>{
console.log(this===a)//false
}
a['add']=bind(add,a)
a.add()
那么下面我们来看它源码:
export function bind (fn: Function, ctx: Object): Function { //兼容重写bind方法
function boundFn (a) {
const l: number = arguments.length
return l
? l > 1
? fn.apply(ctx, arguments)
: fn.call(ctx, a)
: fn.call(ctx)
}
boundFn._length = fn.length
return boundFn
}
function initMethods (vm: Component) {
const methods = vm.$options.methods
if (methods) {
for (const key in methods) {
vm[key] = methods[key] == null ? noop : bind(methods[key], vm) //如果是箭头函数此处用不了bind,改不了this指向,(fn,vm)/vm.fn,this就指向vm
if (process.env.NODE_ENV !== 'production' && methods[key] == null) {
warn(
`method "${key}" has an undefined value in the component definition. ` +
`Did you reference the function correctly?`,
vm
)
}
}
}
}