简介:JS 对于每位前端开发都是必备技能,在小册中我们也会有多个章节去讲述这部分的知识。首先我们先来熟悉下 JS 的一些常考和容易混乱的基础知识点。原始(Primitive)类型涉及面试题:原始类型有哪几种?null 是对象嘛 ...
JS 对于每位前端开发都是必备技能,在小册中我们也会有多个章节去讲述这部分的知识。首先我们先来熟悉下 JS 的一些常考和容易混乱的基础知识点。原始(Primitive)类型涉及面试题:原始类型有哪几种?null 是对象嘛?在 JS 中,存在着 6 种原始值,分别是:
对象(Object)类型涉及面试题:对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?在 JS 中,除了原始类型那么其他的都是对象类型了。对象类型和原始类型不同的是,原始类型存储的是值,对象类型存储的是地址。当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址。 const a = []对于常量 a 来说,假设内存地址为 #001,那么在地址 #001 的位置存放了值 [],常量 a 存放了地址 #001,再看以下代码 const a = []const b = ab.push(1)当我们将变量赋值给另外一个变量时,复制的是原本变量的地址,也就是说当前变量 b 存放的地址也是 #001,当我们进行数据修改的时候,就会修改存放在地址 #001 上的值,也就导致了两个变量的值都发生了改变。接下来我们来看函数参数是对象的情况 function test(person) { person.age = 26 person = { name: "yyy", age: 30 } return person}const p1 = { name: "yck", age: 25}const p2 = test(p1)console.log(p1) // -> ?console.log(p2) // -> ?对于以上代码,你是否能正确的写出结果呢?接下来让我为了解析一番:
typeof vs instanceof涉及面试题:typeof 是否能正确判断类型?instanceof 能正确判断对象的原理是什么?typeof 对于原始类型来说,除了 null 都可以显示正确的类型 typeof 1 // "number"typeof "1" // "string"typeof undefined // "undefined"typeof true // "boolean"typeof Symbol() // "symbol"typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型 typeof [] // "object"typeof {} // "object"typeof console.log // "function"如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof,因为内部机制是通过原型链来判断的,在后面的章节中我们也会自己去实现一个 instanceof。 const Person = function() {}const p1 = new Person()p1 instanceof Person // truevar str = "hello world"str instanceof String // falsevar str1 = new String("hello world")str1 instanceof String // true对于原始类型来说,你想直接通过 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 也不是百分之百可信的。 类型转换涉及面试题:该知识点常在笔试题中见到,熟悉了转换规则就不惧怕此类题目了。首先我们要知道,在 JS 中类型转换只有三种情况,分别是:
转Boolean在条件判断时,除了 undefined, null, false, NaN, "", 0, -0,其他所有值都转为 true,包括所有对象。对象转原始类型对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:
let a = { valueOf() { return 0 }, toString() { return "1" }, [Symbol.toPrimitive]() { return 2 }}1 + a // => 3 四则运算符加法运算符不同于其他几个运算符,它有以下几个特点:
1 + "1" // "11"true + true // 24 + [1,2,3] // "41,2,3"如果你对于答案有疑问的话,请看解析:
"a" + + "b" // -> "aNaN"因为 + "b" 等于 NaN,所以结果为 "aNaN",你可能也会在一些代码中看到过 + "1" 的形式来快速获取 number 类型。那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字 4 * "3" // 124 * [] // 04 * [1, 2] // NaN 比较运算符
let a = { valueOf() { return 0 }, toString() { return "1" }}a > -1 // true在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值。 this涉及面试题:如何正确判断 this?箭头函数的 this 是什么?this 是很多人会混淆的概念,但是其实它一点都不难,只是网上很多文章把简单的东西说复杂了。在这一小节中,你一定会彻底明白 this 这个概念的。我们先来看几个函数调用的场景 function foo() { console.log(this.a)}let a = 1foo()const obj = { a: 2, foo: foo}obj.foo()const c = new foo()接下来我们一个个分析上面几个场景
function a() { return () => { return () => { console.log(this) } }}console.log(a()()())首先箭头函数其实是没有 this 的,箭头函数中的 this 只取决包裹箭头函数的第一个普通函数的 this。在这个例子中,因为包裹箭头函数的第一个普通函数是 a,所以此时的 this 是 window。另外对箭头函数使用 bind 这类函数是无效的。最后种情况也就是 bind 这些改变上下文的 API 了,对于这些函数来说,this 取决于第一个参数,如果第一个参数为空,那么就是 window。那么说到 bind,不知道大家是否考虑过,如果对一个函数进行多次 bind,那么上下文会是什么呢? let a = {}let fn = function () { console.log(this) }fn.bind().bind(a)() // => ?如果你认为输出结果是 a,那么你就错了,其实我们可以把上述代码转换成另一种形式 // fn.bind().bind(a) 等于let fn2 = function fn1() { return function() { return fn.apply() }.apply(a)}fn2()可以从上述代码中发现,不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定,所以结果永远是 window。 let a = { name: "yck" }function foo() { console.log(this.name)}foo.bind(a)() // => "yck"以上就是 this 的规则了,但是可能会发生多个规则同时出现的情况,这时候不同的规则之间会根据优先级最高的来决定 this 最终指向哪里。首先,new 的方式优先级最高,接下来是 bind 这些函数,然后是 obj.foo() 这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。如果你还是觉得有点绕,那么就看以下的这张流程图吧,图中的流程只针对于单个规则。 |