共计 3716 个字符,预计需要花费 10 分钟才能阅读完成。
1. 原始类型
boolean number string null undefined symbol 原始类型储存的都是值,是没有函数可以调用的,undefined.toString() 显示报错,但是 ’1′.toString() // “1”;是因为 ’1’ 被强制转换成了 string 类型,已经不是原始类型而是对象类型了;用 typeOf 可以判断除了 null 的原始类型,typeOf null // OBJECT 判断 null 为 object 类型,其实这是 js 的一个 bug,JS 的最初版使用 32 位系统,为了性能考虑使用低位储存变量信息,000 开头表示对象,而 null 都是 0,所以判断 null 显示为对象。
2. 对象类型
对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?
再 JS 中除了原始类型之外都是对象类型,原始类型储存的是值,而对象类型储存的是地址 (指针),
const a = [];
我们假设常量 a 的指针是 #001,那么 #001 存放了值是 [];
const a = [];
const b = a;
b.push(1);
根据上面所说,我们将常量 a 的值赋给 b,其实是将 a 的指针传给了 b, 即 b 也变成了 #001,那么当我们改变 b 的值其实是改变了#001 的值,同样会导致 a 的值发生变化;
接下来我们看看函数参数是对象的情况来看看
function test(person) {
person.age = 26
person = {
name: ‘yyy’,
age: 30
}
return person
}
const p1 = {
name: ‘wpp’,
age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
答案是什么呢?我们来分析一下首先,函数传参是传递对象指针的副本,到函数内部修改参数的属性这步,p1 的值也被修改了,但是我们重新为 person 分配了一个对象时就出现了分歧,所以最后 person 拥有了一个新的指针,也就和 p1 没有任何关系了,导致最终两个的值是不一样的。
3.typeOf vs instanceof
typeof 是否能正确判断类型?instanceof 能正确判断对象的原理是什么?
typeOf 对于原始类型除了 null 之外都能正确判断,而对于判断对象,除了函数都会显示 object,所以我们不能使用 typeOf 来判断对象的类型;如果我们想去判断一个原始类型我们可以使用 instanceof 来判断;其内部机制是通过原型链来判断的对于原始类型我们直接使用 instanceof 来判断是不行的,当然我们还要办法使用 instanceof 来判断原始类型;
class PrimitiveString {
static [Symbol.hasInstance](x) {
return typeof x === ‘string’
}
}
console.log(‘hello world’ instanceof PrimitiveString) // true
你可能不知道 Symbol.hasInstance 是什么东西,其实就是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof ‘hello world’ === ‘string’,所以结果自然是 true 了。这其实也侧面反映了一个问题,instanceof 也不是百分之百可信的。
4.This
如何正确判断 this?箭头函数的 this 是什么?
谁最后调用了 this,this 就指向谁
function foo() {
console.log(this.a)
}
var a = 1
foo()
const obj = {
a: 2,
foo: foo
}
obj.foo()
const c = new foo()
我们来分析一下上面的级格场景
直接调用 foo() ,this 指向 windows;
obj.foo obj 最后调用 foo,this 指向 obj;
对于 new 来说 this 被绑定在了 c 上面。不会被改变;
接下来我们看看箭头函数中的 this 指向情况,
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
首先箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this。在这个例子中,因为包裹箭头函数的第一个普通函数是 a,所以此时的 this 是 window。另外对箭头函数使用 bind 这类函数是无效的。
最后种情况也就是 bind 这些改变上下文的 API 了,对于这些函数来说,this 取决于第一个参数,如果第一个参数为空,那么就是 window。以上就是 this 的规则了,但是可能会发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定 this 最终指向哪里。
首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。
5.== vs ===
== 和 === 有什么区别?
对于 == 来说,如果对比双方的类型不一样的话,就会进行类型转换对于 === 来说就简单多了,就是判断两者类型和值是否相同
6. 闭包
什么是闭包?
简单来讲就是函数 A 内有函数 B,并且函数 B 可以访问到函数 A 中的变量,函数 B 就叫做闭包。JS 中闭包存在的意义是什么呢?是为了让我们间接访问到函数内部的变量,一个面试经典问题
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
首先因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6。第一种方式
for (var i = 1; i <= 5; i++) {
;(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
第二种方式
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
直接使用 let 来代替 var 也能解决这个问题
7. 深拷贝和浅拷贝
什么是浅拷贝?如何实现浅拷贝?什么是深拷贝?如何实现深拷贝?
我们了解了对象类型在赋值的过程中其实是复制了地址,从而会导致改变了一方其他也都被改变的情况。通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个情况
浅拷贝
首先可以通过 Object.assign 来解决这个问题,很多人认为这个函数是用来深拷贝的。其实并不是,Object.assign 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
我们还可以使用 … 运算符来解决这个问题
let a = {
age: 1
}
let b = {…a}
a.age = 2
console.log(b.age) // 1
通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就可能需要使用到深拷贝了
let a = {
age: 1,
jobs: {
first: ‘FE’
}
}
let b = {…a}
a.jobs.first = ‘native’
console.log(b.jobs.first) // native
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到最开始的话题了,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。
深拷贝
这个问题通常可以通过 JSON.parse(JSON.stringify(object)) 来解决。但是这个方法也有局限性
会忽略 undefined
会忽略 symbol
不能序列化函数
不能解决循环引用的问题
但是大部分情况这个方法都可以适用
8. 原型
如何理解原型?如何理解原型链?
每个 JS 对象都有 proto 属性,这个属性指向了原型。这个属性在现在来说已经不推荐直接去使用它了,这只是浏览器在早期为了让我们访问到内部属性 [[prototype]] 来实现的一个东西。好像还是没有弄明白什么是原型,接下来让我们再看看 proto 里面有什么吧。
看到这里我们会发现原型也是对象,并且这个对象中包含了很多函数,所以我们可以得出一个结论:对于 obj 来说,可以通过 proto 找到一个原型对象,在该对象中定义了很多函数让我们来使用。在上面的图中我们还可以发现一个 constructor 属性,也就是构造函数打开 constructor 属性我们又可以发现其中还有一个 prototype 属性,并且这个属性对应的值和先前我们在 proto 中看到的一模一样。所以我们又可以得出一个结论:原型的 constructor 属性指向构造函数,构造函数又通过 prototype 属性指回原型,但是并不是所有函数都具有这个属性,Function.prototype.bind() 就没有这个属性。
https://juejin.im/post/5c4fdb… 原型的详细介绍
9. 类型转换
再 JS 中类型转换只分三种情况:转换成布尔值,转换成数字,转换成字符串;