“ superxiaoming云 - 当广度了解足够之后,是时候转向深度研究了。”
一、typeof,instanceof
相信这两个运算符大家并不陌生,typeof用来表示操作数的类型,返回一个字符串,instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,用法如下所示:
// typeof
console.log(typeof a); //'undefined'
console.log(typeof(true)); //'boolean'
console.log(typeof '123'); //'string'
console.log(typeof 123); //'number'
console.log(typeof NaN); //'number'
console.log(typeof null); //'object'
var obj = new String();
console.log(typeof(obj)); //'object'
var fn = function(){};
console.log(typeof(fn)); //'function'
console.log(typeof(class c{})); //'function'
// instanceof
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car); // true
console.log(auto instanceof Object); // true
在typescript没火起来之前,js的类型检查主要就靠以上两种运算符来做,让我们看看尤大大的Vue2.0版本源码中的使用:
/**
* Check if value is primitive.
*/
function isPrimitive (value) {
return (
typeof value === 'string' ||
typeof value === 'number' ||
// $flow-disable-line
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
大家感兴趣可以自行搜索一下Vue2.0源码中typeof和instanceof的使用,在很多函数的判断中都有使用,类型判断可以让我们规避掉很多编译阶段发现不了的错误并返回正确的提示,因此掌握是很有必要的。顺带一提,Vue2.0版本源码有使用flow(类似于typesript)来做基础类型判断,它可以在编译阶段发现类型错误并给出提示,配合typeof与instance,Vue2.0源码基本做到了无懈可击(吹一波)。
二、this指向
关于this指向这个东西,前端coder应该都十分熟悉,但也有不少人对其用法概念比较模糊,这里就详细介绍一下方便大家理解,首先大家记住两点:
- this永远指向一个对象
- this的指向完全取决于函数调用的位置
第一点很好理解,this不管在哪儿使用都会指向某一个对象,但是它指向哪个对象呢,第二点就解释了,js一切皆为对象,运行环境也是对象,函数是在某个对象下运行的,而this就是指向函数运行时所在的对象。当然还有一种情况,直接在全局使用this而不是在函数内使用,这个时候this就会指向调用它的对象也就是全局对象。下面举几个列子看一下就更清晰了:
/**
* 浏览器中运行时,指向Window对象,因为全局中
* 使用时,调用此语句的对象为全局对象Window
*/
console.log(this) // Window()
/**
* 调用person1的sayHello时,sayHello运行时所在对
* 象为person1,所以this指向person1,因此返回leo21
* 同理调用person2的sayHell,sayHello运行时所在对象
* 为person2,所以this指向person2,因为person2构造
* 时使用了默认参数,所以返回jack18
*/
function person(name = 'jack', age = 18){
this.name = name;
this.age = age;
this.sayHello = function(){
console.log(this.name + this.age);
}
}
let person1 = new person("leo", 21);
person1.sayHello(); // leo21
let person2 = new person();
person2.sayHello(); // jack18
正常情况下this的判断就是这么简单,那么哪些情况下会不正常呢,下面举几个不正常的例子:
- setTimeout、setInterval
// 当在浏览器中执行这段代码后,你会发现this指向了Window
var c = 5;
function man(){
setTimeout(function(){
console.log(this.c);
}, 1000)
};
var people = {
c: 6,
man: man
};
people.man(); // 5
为什么会这样呢,关于这个,MDN给出的解释是这样的
所以才会出现上面代码中的情况,大家在开发的时候要注意这点,避免这个“坑”的方式有两种,如下所示:
/**
* 1、使用self存储this的引用,当在setTimeout中
* 调用时,self就会使用man函数中this的指向,从
* 而指向people对象,因此返回6
*/
var c = 5
function man(){
var self = this
setTimeout(function(){
console.log(self.c)
}, 1000)
}
var people = {
c: 6,
man: man
}
people.man() // 6
/**
1. 2、箭头函数内部的this会指向声明箭头函数时所
2. 在作用域的this,man函数中的this指向people,
3. 所以箭头函数中this指向people,因此返回6
*/
var c = 5
function man(){
setTimeout(() => {
console.log(this.c)
}, 1000)
}
var people = {
c: 6,
man: man
}
people.man()
- 箭头函数(上面例子说过了,这里就不细说了)
- call,apply,bind
这三个方法都是改变this指向的,在源码中也是非常常见的,下面说一下他们的区别:
call调用 将方法中的this指向call中第一个参数,当第一个参数为null、undefined时,默认指向window; call中第一个参数之后是要传递给方法的参数列表。
apply与call相似,不同之处在于传递给方法的参数形式不一致。apply传递给方法的参数是数组的形式。
call和apply在改变方法的this指向时,会同时执行方法;而bind不会执行方法,而是返回改变this指向后的新方法。
var x = "window_x";
var obj = {
x : "obj_x",
fn : function(y){
console.log(this.x + " ; " + y);
}
}
obj.fn(1); //obj_x ; 1
var fn = obj.fn;
fn(1); //window_x ; 1
fn.call(obj,1); //obj_x ; 1
fn.apply(obj,[1]); //obj_x ; 1
fn.bind(obj,1); //返回fn方法:f (y){console.log(this.x + " ; " + y);}
var bfn = fn.bind(obj);
bfn(1); //obj_x ; 1
以上就是关于this的使用以及一些注意点的总结了,掌握this的使用非常重要,无论是阅读源码还是日常开发都十分关键,希望大家好好了解一波。
结语
这次的关于源码阅读基础准备分享就到这里,下次再继续介绍其他相关需要了解的知识点,喜欢的可以关注一波微信公众号:Js之美~