当前位置: 首页 > 工具软件 > BFE > 使用案例 >

BFE——JS题库刷题经历

江佐
2023-12-01

我原先以为我的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
 类似资料: