我原先以为我的js虽然学的不算好,但也绝对还行。直到我在这个题库里写了两道题。。。
怎么说呢,这个题库里的题是真的好,可以让我们以刷题的方式掌握很多js平时注意不到的知识点,下面是我这段时间刷题的经历,这篇博客只是为了记录一下学习经历,绝对不是为了国庆勋章水博客,嗯,绝对不是。
题库链接JavaScript quizzes | BFE.dev - prepare for Front-End job interviews.
97. `this` V
const obj = {
prefix: 'BFE',
list: ['1', '2', '3'],
log() {
this.list.forEach(function (item) {
console.log(this.prefix + item);
});
},
};
obj.log();
//"undefined1"
//"undefined2"
//"undefined3"
这里log函数内部的this指向obj,this.list.forEach内部的匿名函数中的this指向log函数,因此找不到prefix,输出为undefined。
96. comparison
console.log(10 > 9)
console.log(10 > '9')
console.log('10' > 9)
console.log('10' > '9')
//true
//true
//true
//false
js在使用>号进行判断时,如果两边有一边是字符一边是数字,会将字符转换为数字进行比较。要是两边都是字符,通过字典顺序进行比较,‘10’第一个字是‘1’比‘9’小,因此‘10’<'9'
95. number format
console.log(017 - 011)
console.log(018 - 011)
console.log(019 - 011)
//6
//9
//10
因为前导0存在,默认表示八进制,因此第一个为15-9 = 6. 但是第二行中,出现了8,因此第二行前面的数被认为是正常的十进制,后面是八进制,18-9 = 9
最后一行同理,19-9 = 10
94. emoji
console.log('👍'.length)
//2
JavaScript字符串使用UTF-16代码单元表示。每个代码单元都可以用来表示[U+0000, U+ FFFF]范围内的一个代码点——也称为“ 基本多语言平面”(BMP)。
对于U+ FFFF以外的代码点,可以将它们表示为代理对。也就是说,两个连续的代码单元。
字符串的.length属性返回字符串中代码单元的数量。虽然这通常与字符长度相同,但使用BMP([U+0000,U+FFFF]范围外的字符会返回不同的长度。
93. string
let a = 'bfe.dev'
a[0] = 'c'
console.log(a)
//"bfe.dev"
从ES5开始,可以将字符串视为类似数组的对象,其中字符对应于一个数字索引。但是,当使用括号符号进行字符访问时,尝试删除或赋值。这些属性不会成功。所涉及的属性既不能写,也不能配置。
92. NaN
console.log(NaN == NaN)
console.log(NaN === NaN)
console.log(Object.is(NaN, NaN))
console.log([NaN].indexOf(NaN))
console.log([NaN].includes(NaN))
console.log(Math.max(NaN, 1))
console.log(Math.min(NaN, 1))
console.log(Math.min(NaN, Infinity))
//false
//false
//true
//-1
//true
//NaN
//NaN
//NaN
因为NaN与任何元素使用== 和===相比较都为false,哪怕另一个元素也是NaN.因此前两行输出都是NaN
Object.is()用来确定两个数是否值是一样的,并且当我们比较两个NaN的时候,Object.is()会返回true,因此第三行输出true
第四行使用indexOf(),而indexOf(NaN)===-1,第五行array.includes使用了SameValueZero比较算法,从而使includes(NaN)为true
Math.max(),Math.min()如果任何一个参数不是数字,就会返回NaN。
91. largest Array index
const arr = []
arr[(2 ** 32) - 2] = 1
arr[(2 ** 32) - 1] = 2
console.log(arr.at(-1))
//1
at()方法接受一个整数值并返回该索引处的项,这个整数值可以是正的也可以是负的,如果是负整数,从数组中的最后一项开始倒数。
例:对于最后一项array[array.length-1]也通过可以调用array.at(-1)来访问
JavaScript数组是从零开始的,使用32位索引:第一个元素的索引是0,最高的索引是4294967294(2^32−2),我们将其赋值为1
访问时,arr.at(-1)返回最后一个元素。
另外,请注意arr[2^32 - 1] = 2确实将2存储在该索引上,但是因为它超过了at所能访问的最大长度,所以访问不到这个索引。
90. array keys
console.log(Reflect.ownKeys([]))
console.log(Reflect.ownKeys([,]))
console.log(Reflect.ownKeys([1,,2]))
console.log(Reflect.ownKeys([...[1,,2]]))
// ["length"]
// ["length"]
// ["0","2","length"]
// ["0","1","2","length"]
静态的Reflect.ownKeys()方法返回目标对象自身属性键的数组。
JavaScript中底层的数组是对象。它们的键是数字,值是元素。默认情况下,所有数组都有一个反映数组中元素数量的属性长度。在稀疏数组的情况下,孔不会将相应的键添加到数组中。
此外,扩展运算符会将这些孔转换为undefined
在第一个输出中,它是一个空数组,所以answer只包含长度
在第二个输出中,数组[,]也是一个空的稀疏数组,因此孔被忽略,返回["length"]
在第三个数组中,[1,,2]在索引0和2处定义值,因此输出["0","2","length"]
在最后一个数组中,使用扩展运算符将输入数组更改为[1,undefined,2],因此所有键都给出以下输出["0","1","2","length"]
89. let
let a = 1;
(function() {
let foo = () => a
let a = 2;
console.log(foo())
}())
//2
在这个立即执行函数里面,函数foo()只执行一个操作,就是返回a,返回调用这个函数时所在作用域的a。因此输出是2.
88. try...catch
var a = 'a'
try {
throw new Error('BFE.dev')
} catch {
var a = 'a1'
}
console.log(a)
var b = 'b'
try {
throw new Error('BFE.dev')
} catch (b) {
var b = 'b1'
}
console.log(b)
var c = 'c'
try {
throw new Error('BFE.dev')
} catch (error) {
var c = 'c1'
}
console.log(c)
try-catch 语句,作为 JavaScript 中处理异常的一种标准方式。基本的语法如下所示:
try{
// 可能会导致错误的代码
} catch(error){
// 在错误发生时怎么处理
}
var a = 'a'
try {
throw new Error('BFE.dev')
} catch { // 没有本地变量被使用
var a = 'a1' // 重写外部变量a,重新声明全局变量a
}
console.log(a) // a1
var b = 'b'
try {
throw new Error('BFE.dev')
} catch (b) { // 局部变量b被引用
var b = 'b1' // 不再指向全局变量,它只是一个局部作用域变量
}
console.log(b) // b
var c = 'c'
try {
throw new Error('BFE.dev')
} catch (error) { // 引用局部变量error传递错误
var c = 'c1' // 重写外部变量c,重新声明全局变量c
}
console.log(c) // c1
87. instanceOf 2
console.log(Function instanceof Object)
console.log(Object instanceof Function)
console.log(Function instanceof Function)
console.log(Object instanceof Object)
//true
//true
//true
//true
instanceof 判断的原理是:通过左侧对象的隐式原型属性 __ proto __ 在原型链上向上一层层查找,找到右侧构造函数的原型对象属性 prototype 就返回 true。
86. setTimeout III
let func = () => {
console.log(1)
}
setTimeout(() => {
func = () => {
console.log(2)
}
}, 0)
setTimeout(func, 100)
//1
由于js执行遵循事件循环机制,因此先执行同步任务,再执行异步任务。当第一个异步函数改变func函数的时候,setTimeout(func, 100)早已被放入执行队列,因此在setTimeout(func, 100)中的func仍是一开始定义的func函数,所以输出为1.
85. String.raw()
console.log(String.raw`BFE\n.${'dev'}`) // "BFE\n.dev"
console.log(String.raw({raw: 'BFE'}, 'd', 'e','v')); // "BdFeE"
静态的String.raw()方法是模板字面量的标记函数。它用于获取模板字面量的原始字符串形式,也就是说,替换(例如${foo})会被处理,但转义(例如\n)不会被处理。注意,它的工作原理与默认模板函数类似,并执行字符串拼接。重要的区别是它不会转义字符。
console.log("BFE\ndev") // \n会被转义
// BFE
// dev
console.log(String.raw`BFE\ndev`) // 这里的\n会保持原样
// BFE\ndev
String.raw()的工作原理类似于一个交织函数。第一个参数是一个具有原始属性的对象,其值是一个可迭代对象(可以是字符串或数组),表示模板字面量中分离的字符串。剩下的参数是替换。额外的替换被忽略
// 使用数组
String.raw({ raw: [0,2,4] }, 1, 3, 5, 6, 7) // "01234"
// 使用字符串
String.raw({ raw: '024' }, 1, 3, 5, 6, 7) // "01234"
84. Array.prototype.sort()
const a = [999, 1111, 111, 2, 0]
const b = a.sort()
console.log(a)
console.log(b)
// [0,111,1111,2,999]
// [0,111,1111,2,999]
sort()函数将a进行了原地排序,因为里面没有写排序方式,因此是将每个元素根据Unicode编码进行排序。
如果想要顺序排序
const b = a.sort((x,y)=>x-y);
83. Plus Plus
console.log(1 + 1) // 2
console.log(1 + + 1) // 1 + (+1) = 1 + 1 = 2
console.log(1 + + 1 + 1) // 1 + (+1) + 1 = 1 + 1 + 1 = 3
console.log(1 + + 1 + + 1) // 1 + (+1) + (+1) = 1 + 1 + 1 = 3
console.log(1 + + + 1) // 1 + (+(+1)) = 1 + (+1) = 1 + 1 = 2
console.log(1 + + '1' + + '1') // 1 + (+'1') + (+'1') = 1 + 1 + 1 = 3
console.log('1' + + '1' + + '1') // "1" + (+'1') + (+'1') = "1" + 1 + 1 = "1" + "1" + "1" = "111"
console.log('a' + + 'b') // "a" + (+'b') = a + "NaN" = "aNaN"
console.log('a' + + 'b' + 'c') // "a" + (+'b') +'c' = a + "NaN" + "c" = "aNaNc"
console.log('a' + + 'b' + + 'c') // "a" + (+'b') + (+'c') = a + "NaN" + "NaN" = "aNaNNaN"
82. Proxy II
class Dev {
#name
constructor(name) {
this.#name = name
}
get name() {
return this.#name;
}
}
const dev = new Dev('BFE')
console.log(dev.name) // "BFE"
const proxyDev = new Proxy(dev, {})
console.log(proxyDev.name) // Error
私有类成员可以通过使用hash#前缀来创建。私有字段可以在类构造函数上从类声明本身内部访问,而不能从派生子类访问。
代理无法从dev对象读取私有成员#name。
81. setTimeout II
let num
for (let i = 0; i < 5; i++) {
num = i
setTimeout(() => {
console.log(num)
}, 100)
}
//4
//4
//4
//4
//4
由于setTimeout()是异步的,因此当他执行的时候,for循环已经执行完毕,此时打印出来的num都是4
80. Proxy I
const obj = new Map()
const map = new Map()
obj.foo = 1
map.set('foo', 2)
console.log(obj.foo) //1
console.log(map.get('foo')) //2
const proxyObj = new Proxy(obj, {})
const proxyMap = new Proxy(map, {})
console.log(proxyObj.foo) //1
console.log(proxyMap.get('foo')) //Error
Proxy对象允许您创建一个可用于代替原始对象的对象,但它可能重新定义基本的对象操作,如获取、设置和定义属性。
如果没有定义处理程序,则默认行为是将操作转发到目标,但这只适用于诸如属性访问等标准行为,而不适用于外来对象的内部插槽。
如果目标没有[[MapData]]内部插槽,则抛出TypeError异常。这里也对此进行了解释
79. Equal III
console.log(2.0 == "2" == new Boolean(true) == "1")
// 2 == 2 == true == "1"
// true == true == "1"
// true == "1"
// 1 == 1
// true