壹题汇总每日五题 | 2021.08.24 有疑问

194 阅读4分钟

第 43 题:使用 sort() 对数组 [3, 15, 8, 29, 102, 22] 进行排序,输出结果

结果为[102,15,22,29,3,8]。 若想按数组大小排序,应为

//从大到小
[3, 15, 8, 29, 102, 22].sort((a,b)=>b-a);//[102, 29, 22, 15, 8, 3]

//从小到大
[3, 15, 8, 29, 102, 22].sort((a,b)=>a-b);//[3, 8, 15, 22, 29, 102]
/*
-   返回值大于0 即a-b > 0 , a 和 b 交换位置
-   返回值大于0 即a-b < 0 , a 和 b 位置不变
-   返回值等于0 即a-b = 0 , a 和 b 位置不变
*/

第 46 题:输出以下代码执行的结果并解释为什么

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice': Array.prototype.splice,
    'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)

应当输出

{
    '2': 3,
    '3': 4,
     2 : 1,
     3 : 2,
    'length': 4,
    'splice': Array.prototype.splice,
    'push': Array.prototype.push
 }
 //【错误】
 
 
 Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ]
 //【正确】

push方法影响数组的length属性和对应下标的值。length为2,故从第2项开始插入:

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)  //  {2: 1, 3: 2, length: 4, push: ƒ}
//【为什么'2': 3,'3': 4显示不出来?】

【下面这个例子是为什么?】

var obj = {
    '2': 3,
    '3': 4,
    'push': Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)  // {0: 1, 1: 2, 2: 3, 3: 4, length: 2, push: ƒ}

在对象中加入splice属性方法,和length属性后。这个对象变成一个类数组。

故而输出 Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ]。实际上它是一个伪数组,并没有数组的其他属性和方法。【为什么JSON.stringify可以验证?】

  1. 使用第一次push,obj对象的push方法设置 obj[2]=1;obj.length+=1 
  2. 使用第二次push,obj对象的push方法设置 obj[3]=2;obj.length+=1 
  3. 使用console.log输出的时候,因为obj具有 length 属性和 splice 方法,故将其作为数组进行打印
  4. 打印时因为数组未设置下标为 0 1 处的值,故打印为empty,obj[0] 获取为 undefined。

第 48 题:call 和 apply 的区别是什么,哪个性能更好一些

  1. Function.prototype.apply和Function.prototype.call 的作用是一样的,区别在于传入参数的不同;
  2. 第一个参数都是,指定函数体内this的指向;
  3. 第二个参数开始不同,apply是传入带下标的集合,数组或者类数组,apply把它传给函数作为参数;call从第二个开始传入的参数是不固定的,都会传给函数作为参数。
  4. call比apply的性能要好,内部少了一次将 apply 第二个参数解构的操作。平常可以多用call, call传入参数的格式正是内部所需要的格式,参考call和apply的性能对比。 尤其是es6 引入了 Spread operator (延展操作符) 后,即使参数是数组,也可以使用 call。
let params = [1,2,3,4]
xx.call(obj, ...params)

第 53 题:输出以下代码的执行结果并解释为什么

var a = {n: 1};
var b = a;
a.x = a = {n: 2};  //?

console.log(a.x) 	
console.log(b.x)

都输出{n:2}。【错误】

输出 undefined;{n:2}.【正确】

a.x = a = {n: 2};首先获取等号左侧的a.x,但a.x并不存在,于是JS为(堆内存中的)对象创建一个新成员x,这个成员的初始值为undefined。创建完成后,目标指针已经指向了这个新成员x,【谁是目标指针?】并会先挂起,单等等号右侧的内容有结果了,便完成赋值。

接着执行赋值语句的右侧,发现a={n:2}是个简单的赋值操作,于是a的新值等于了{n:2}。【为什么a.x=a不先操作?】

这里特别注意,这个a已经不是开头的那个a,而是一个全新的a,这个新a指针已经不是指向原来的值的那个堆内存,而是分配了一个新的堆内存。但是原来旧的堆内存因为还有b在占用,所以并未被回收。

然后,将这个新的对象a的堆内存指针,赋值给了刚才挂起的新成员x,此时,对象成员x便等于了新的对象a。

所以,现在b={n:1,x:{n:2}};a={n:2};a===b.x(true,注意对象的相等,不是值的相等,而是引用的相等,也就是说,相等表示指针是指向同一个堆内存。)

第 58 题:箭头函数与普通函数(function)的区别是什么?构造函数(function)可以使用 new 生成实例,那么箭头函数可以吗?为什么?

简写。不可以。【为什么?】

箭头函数:
1、函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

2、不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

3、不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

4、不可以使用 new 命令,因为:

  • 没有自己的 this,无法调用 call,apply。
  • 没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 proto

new 过程大致是这样的:

function newFunc(father, ...rest) {
  var result = {};
  result.__proto__ = father.prototype;
  var result2 = father.apply(result, rest);
  if (
    (typeof result2 === 'object' || typeof result2 === 'function') &&
    result2 !== null
  ) {
    return result2;
  }
  return result;
}