JavaScript之this

71 阅读3分钟

全局上下文

非严格模式和严格模式,this都是指向顶层对象的(window)

函数上下文

普通函数调用

// 非严格模式
var name = 'window';
var doSth = function(){
    console.log(this.name);
}
doSth(); // 'window'

// 使用var 全局变量挂在在window上

// 非严格模式
let name2 = 'window2';
let doSth2 = function(){
    console.log(this === window);
    console.log(this.name2);
}
doSth2() // true, undefined
// let 没有给顶层对象window 添加属性,所以window.name2和window.doSth2 都是undefined

再看看严格模式下

// 严格模式
'use strict'
var name = 'window';
var doSth = function(){
    console.log(typeof this === 'undefined');
    console.log(this.name);
}
doSth(); // true,// 报错,因为this是undefined

// 默认绑定的原因
doSth.call(undefined);
doSth.apply(undefined);

callapply作用之一就是用来修改函数中的this指向为第一个参数的。第一个参数是undefined或者null,非严格模式下,只想window,严格模式下,指向第一个参数。

例如回调函数

var name = '若川';
setTimeout(function(){
    console.log(this.name);
}, 0);
// 语法
setTimeout(fn | code, 0, arg1, arg2, ...)
// 也可以是一串代码。也可以传递其他函数
// 类比 setTimeout函数内部调用fn或者执行代码`code`。
fn.call(undefined, arg1, arg2, ...);

对象中的函数(方法)调用模式

var name = 'window';
var doSth = function(){
    console.log(this.name);
}
var student = {
    name: '若川',
    doSth: doSth,
    other: {
        name: 'other',
        doSth: doSth,
    }
}
student.doSth(); // '若川'
student.other.doSth(); // 'other'
// 用call类比则为:
student.doSth.call(student);
// 用call类比则为:
student.other.doSth.call(student.other);

// 但是又有以下场景

var studentDoSth = student.doSth;
studentDoSth(); // 'window'
// 用call类比则为:
studentDoSth.call(undefined);


call apply bind

fun.call(thisArg, arg1, arg2, ...)

  • thisArg

fun函数运行时指定的this值。注意 this 值不一定是函数真正执行的this 值,如果在非严格模式下,指定undefined 的值会自动指向全局变量

同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象

var doSth = function(name){
    console.log(this);
    console.log(name);
}
doSth.call(2, '若川'); // Number{2}, '若川'
var doSth2 = function(name){
    'use strict';
    console.log(this);
    console.log(name);
}
doSth2.call(2, '若川'); // 2, '若川'

严格模式下,thisArg是原始值的值类型,也就是原始值。

call 和 apply 基本一致,参数不一致

bind 的返回值时心函数,新函数也能当做构造函数调用(new)

bind()方法创建一个新的函数, 当这个新函数被调用时this键值为其提供的值,其参数列表前几项值为创建时指定的参数序列。
语法: fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数: thisArg 调用绑定函数时作为this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。 当使用bindsetTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object

如果没有提供绑定的参数,则执行作用域的this被视为新函数的thisArgarg1, arg2, ... 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

返回值 返回由指定的this值和初始化参数改造的原函数拷贝。

构造函数调用模式

function Student(name){
    this.name = name;
    console.log(this); // {name: '若川'}
    // 相当于返回了
    // return this;
}
var result = new Student('若川');

使用new调用函数,会执行一下步骤

  1. 创建全新对象
  2. 这个对象执行[[prototype]]链接
  3. 生成新对象绑定到调用函数的this
  4. 通过new 创建的每个对象将最终被[[prototype]]链接到这个函数的prototype对象上
  5. 如果函数没有返回对象类型object,那么new表达式中的函数会自动返回新的对象

由此可见,new操作符调用时。this只想新生成的对象。特别提醒一下,new调用时的返回值,如果没有显式返回对象或者函数,才是返回生成的新对象

function Student(name){
    this.name = name;
    // return function f(){};
    // return {};
}
var result = new Student('若川');
console.log(result); {name: '若川'}
// 如果返回函数f,则result是函数f,如果是对象{},则result是对象{}

原型链中的调用

function Student(name){
    this.name = name;
}
var s1 = new Student('若川');
Student.prototype.doSth = function(){
    console.log(this.name);
}
s1.doSth(); // '若川'