# js 面试题

# 对象拷贝

  • 深拷贝: 通过利用JSON.parse(JSON.stringify(Object))来达到深拷贝的目的 但是JSON深拷贝的缺点是undefinedfunction还有symbol类型是无法进行深拷贝的 如有需要可以自己手动封装函数来达到目的

  • 浅拷贝: 通过ES6新特性Object.assign()与扩展...运算符来达到浅拷贝的目的

# js继承

借助寄生组合继承

主要实现原理 B.prototype = Object.create(a.prototype) 继承a

当我们new关键字创建实例时,会有B自身的属性和方法以外,还有a原型上的方法

当实例化对象调用某个方法时会先在自身和原型上查找,然后是在_proto_上一层层查找,这种方式就是原型链

# addEventListener

1.给一个事件绑定多个监听器 2.可以控制触发的阶段(选择冒泡或者捕获)

target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);

第三个参数可以传入两种形式,boolean和对象

boolean(useCapture) 默认是false 冒泡阶段 options 有三个属性

  • capture 默认false,冒泡阶段
  • once 默认false 是否只调用一次
  • passive 默认true listener不会调用preventDefault()(阻止默认行为)

EventTarget.addEventListener (opens new window)

# class 和 function 的区别

class创建本质上还是基于原型链, 更加符合面向对象的思维

  1. class的静态方法的this,指向类,并非实例

  2. class定义的方法不能使用object.keys遍历

  3. class不能定义私有属性和方法

  4. class只能用类名调用

# 谈谈作用域链机制

https://segmentfault.com/a/1190000018513150 https://www.cnblogs.com/dolphinX/p/3280876.html

# 箭头函数有没有 arguments 对象

浏览器中执行没有arguments

nodejs中执行是有arguments

# 箭头函数和普通函数的区别

  1. 语法更加简洁,清晰

  2. 箭头函数与普通函数不同,不会有自己的this, 继承this指向外面一层作用域

  3. .call()/.apply()/.bind()无法改变箭头函数中this的指向

  4. 没有自己的arguments

  5. 箭头函数没有原型prototype

# js 精度丢失问题

由于数据存储在内存中是以二进制的形式存储,而某些值转成二进制会无限循环,由于位数有限,所以无限循环会自动四舍五入

0.1 + 0.2  = 0.30000000000000004
// 0.1 转成二进制会无限循环
// "0.000110011001100110011001100110011001100110011001100..."

# js中不同进制怎么转换

10 进制转其他进制:Number(val).toString([2,8,10,16])

其他进制转成10进制:Number.parseInt("1101110",[2,8,10,16])

# 讲一讲 Promise

Promise是es6的规范

1.Promise有三个状态值,不可逆的,等待态(pedding), 执行态(resolve), 拒绝态(reject) 2.提供了一个then方法。返回是个promise 3.可以链式调用

# 判断数组的几种方法

  1. es6语法 Array.isArray()
  2. obj instanceof Array 原型链查找
  3. obj.constructor === Array 构造函数类型判断
  4. Object.prototype.toString.call(obj) === '[object Array]'

# 循环有几种方式,是否支持中断和默认情况下是否支持async/await

for 支持中断、支持异步事件

for of 支持中断、支持异步事件

for in 支持中断、支持异步事件

forEach 不支持中断、不支持异步事件

map 不支持中断、不支持异步事件,支持异步处理方法:map 返回promise数组,在使用 Promise.all 一起处理异步事件数组

// 使用async await 处理异步操作
let results = await Promise.all(arr.map(async (item) => {
// 等待异步操作完成,返回执行结果
return await asyncWorker(item);
}));

reduce 不支持中断、不支持异步事件,支持异步处理方法:返回值返回 promise 对象

# 请写出下面三次alert的结果

<script>
var a = 3
function change1 (a) {
    a = 4
}
change1(a)
alert(a)

var user = {age: 30}
function change2 (user) {
    user.age = 40
}

change2(user)
alert(user.age)

function change3 (user) {
    user = {age: 50}
}
change3(user)
alert(user.age)

</script>

结果

  1. 3 // 原始类型的值赋值,函数中获取的是参数
  2. 40 // 引用类型的地址传入
  3. 40 // 函数中的引用类型的重新赋值

# 右侧写出浏览器console执行完左侧代码以后的alert()内容, 提示接下来三行代码都会弹出三次弹窗

function test(a, b) {
    alert(b) 
    return {
        test: function (c, a) {
            return test(c, a)
        }
    }
}

code
1. var a = test(100, 200); a.test(300); a.test(400)
2. var b = test(101).test(201).test(401)
3. var c = test(102).test(202, 302); c.test()
  1. 200,undefined, undefined

  2. undefined, undefined, undefined

  3. undefined, 302, undefined

# 设置a的值让条件 a==5&& a==8成立

第一种

const a = { value : 2 };
a.valueOf = function() {
    return this.value += 3;
};
console.log(a==5 && a== 8)

  1. 判断类型
  2. 判断null undefined
  3. string == number ? string转number
  4. boolean == any ? boolean转number
  5. object == string ? number ? symbol ? object 调用valueOf toString

第二种

# Object.defineProperty

有两种创建方式, 数据描述符和存储描述符

两者共享的属性 configurable: false 该属性的描述符才能够被改变, 可删除修改 enumerable: false 可枚举

可选 value: undefined writable: false 可赋值

存储数据符 get: undefined set: undefined

window.a 与b = {a: 1}是有区别的 window.a 挂在在Array反而和创建object一样

var a = {b: 1}
Object.getOwnPropertyDescriptor(a, "b");
{value: 1, writable: true, enumerable: true, configurable: true}

a = 1
Object.getOwnPropertyDescriptor(window, "a");
{value: 1, writable: true, enumerable: true, configurable: false}
  1. object.defineProperty 有两种,数据描述符和存储描述符

共有参数configurable, enumerable. 数据描述符多了value, writeable, 存储描述符get, set方法。两者不能混用

2.configurable, 是否重复定义,是否delete, enumerable是否遍历, writeable是否重新赋值

3.window.a的configurable默认为false, 但其他定义对象里面属性为true

4.enumerable为false不可遍历

var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable: true });
Object.defineProperty(o, "b", { value : 2, enumerable: false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable 默认为 false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则 enumerable 为 true
Object.defineProperty(o, Symbol.for('e'), {
  value: 5,
  enumerable: true
});
Object.defineProperty(o, Symbol.for('f'), {
  value: 6,
  enumerable: false
});

for (var i in o) {
  console.log(i);
}
// logs 'a' and 'd' (in undefined order)

Object.keys(o); // ['a', 'd']

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
o.propertyIsEnumerable('d'); // true
o.propertyIsEnumerable(Symbol.for('e')); // true
o.propertyIsEnumerable(Symbol.for('f')); // false

var p = { ...o }
p.a // 1
p.b // undefined
p.c // undefined
p.d // 4
p[Symbol.for('e')] // 5
p[Symbol.for('f')] // undefined
Configurable 属性

参考链接 (opens new window)

# 不使用for或者while, 创建一个长度为120的数组,并且每个元素的值等于数组长度减去它的下标

  1. from + map
Array.from(new Array(120)).map((item,index)=>(120 - index))

2.from

Array.from(new Array(100), (item, index) => index)

3.reduce

Array.from(new Array(120)).reduce(function (pre, v, i, arr) {
    pre.push(arr.length - i)
    return pre
}, [])

# 输出以下代码执行结果,大致时间就好

function wait() {
return new Promise(resolve =>
 setTimeout(resolve, 10 * 1000)
)
}

async function main() {
console.time();
await wait();
await wait();
await wait();
console.timeEnd();
}
main();
30多秒,由于await同步执行,等待执行函数到下一步
function wait() {
  return new Promise(resolve =>
    setTimeout(resolve, 10 * 1000)
  )
}

async function main() {
  console.time();
  let a = wait();
  let b = wait();
  let c = wait();
  await a;
  await b;
  await c;
  console.timeEnd();
}
main();

运行时间是10s多一点 等待时间最长的就是整体时间

# map比set区别

set对象类似数组, Map 对象是键值对集合,和 JSON 对象类似,但是 key 不仅可以是字符串还可以是对象

// set
const arr = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1];
const set = new Set();
arr.forEach(item => set.add(item));
console.log(set);  // 1, 2, 3, 4, 5
// 数组快速去重
console.log([...new Set(arr)]);  //[1, 2, 3, 4, 5]

// map
var map = new Map();
var obj = { name: '小缘', age: 14 };
map.set(obj, '小缘喵');
map.get(obj); // 小缘喵
map.has(obj); // true
map.delete(obj) ;// true
map.has(obj); // false

# 数据结构,链表和数组区别

添加修改删除数组会影响前后值的变化

链表是存储在内存中

# 浏览器的内核

1.Trident内核 (IE) 2.Gecko内核 (Firefox) 3.WebKit内核 (Safari内核,Chrome内核原型) 4.Blink内核 (Opera)