前端面试题系列之-原生js篇

2020-08-11  本文已影响0人  wowoqu

隐式类型转换相关

字符串连接符与算数运算符

关系运算符

关系运算符会把其他数据转换成number之后再比较

逻辑非运算符

各类型隐式转换规则

字符串类型转为数值类型:

闭包相关

什么是闭包

闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量。

优点

缺点

作用域和变量提升相关

作用域

作用域分为全局作用域、函数作用域及块级作用域

变量提升

js在执行一个作用域的代码之前,会先将变量声明及函数声明提到整个作用域顶部,随后依次执行代码,其中若变量与函数同名,函数优先级要高于变量。
即:

作用域链

如果当前作用域中找不到变量,则会想上层作用域查找,依次往上查找,形成作用域链

NaN是什么?有什么特别之处?

NaN 是 not a number 的缩写,代表非数字值的特殊值,该属性用于指示某个值不是数字,是一个全局对象的属性
特别之处:

== 和 === 的区别

两等是值判断,会调用隐式类型转换,不判断类型
三等是值判断加类型判断,判断两个值是否严格相等

typeof和instanceof的区别

typeof

用于判断数据类型,返回值为6个字符串,分别为string、Boolean、number、function、object、undefined。其中null会返回object结果

instanceof

instanceof用来判断对象,判断方法是根据对象的原型链依次向下查询

js的数据类型有哪些

JS原生有5种简单数据类型:UndefinedNullBooleanNumberString。还有一个复杂数据类型Object函数在JS中是对象,而不是一种数据类型

undefined和null的区别

Undefined类型为变量声明了但是没有初始化(var box),而Null表示一个空对象引用(var box = null)。注意:未初始化的变量和赋值为 null 的 变量会相等,可通过typeof或者使用三等进行判断

call、apply、bind的区别

call 和 apply 是为了改变函数体内部 this 的指向。对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。

示例:

function func(arg1, arg2) {};

func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
var obj = {
    a: 123,
};
 
var foo = {
    getV: function(k) {
        return this.a + k;
    }
}
 
console.log(foo.getV.bind(obj, 1)());  //124
console.log(foo.getV.call(obj, 1));    //124
console.log(foo.getV.apply(obj, [1]));   //124

区别:

js的假值有哪些

0-0NaNundefinednull""false

js面向对象与原型、原型链相关

参考链接

原型(对象属性)

Javascript规定,每个函数都有一个prototype对象属性。只有函数才有prototype属性。指向另一个对象(原型链上面的)。prototype(对象属性)的所有属性和方法,都会被构造的实例继承。这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象属性上。

prototype就是调用构造函数所创建的那个实例对象的原型(proto)。

prototype可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

原型链 (JS原型与原型链继承)

实例对象与原型之间的连接,叫做原型链。proto( 隐式连接 )
JS在创建对象的时候,都有一个叫做proto的内置属性,用于指向创建它的函数对象的原型对象prototype。
内部原型(proto)和构造器的原型(prototype)
1、每个对象都有一个proto属性,原型链上的对象正是依靠这个属性连结在一起
2、作为一个对象,当你访问其中的一个属性或方法的时候,如果这个对象中没有这个 方法或属性,那么Javascript引擎将会访问这个对象的proto属性所指向上一个对 象,并在那个对象中查找指定的方法或属性,如果不能找到,那就会继续通过那个对象 的proto属性指向的对象进行向上查找,直到这个链表结束。

浅谈constructor

在 Javascript 语言中,constructor 属性是专门为 function 而设计的,它存在于每一个 function 的prototype 属性中。这个 constructor 保存了指向 function 的一个引用。

__proto__

JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做 __proto__ 的内置属性,用于指向创建它的构造函数的原型对象。

    var obj = []
    console.log(obj.__proto__ === Array.prototype)  //true

示例题

    function Foo() {}
    var f1 = new Foo()

请回答以下问题:

  1. f1.__proto__ ===
  2. f1.constructor ===
  3. f1.prototype ===
  4. Foo.__proto__ ===
  5. Foo.prototype ===
  6. Foo.constructor ===
  7. Foo.prototype.constructor ===
  8. Foo.prototype.__proto__ ===

继承的几种写法及优缺点

JS继承的实现方式

this指向相关

js new一个对象都经过了什么

new对象:

function Person(name, age) {
  this.name = name;
  this.age = age;
}
var person = new Person("Alice", 23);

new一个对象的四个过程:1、创建一个空对象?

var obj = new Object();

2、让Person中的this指向obj,并执行Person的函数体?

var result = Person.call(obj);

3、设置原型链,将obj的proto成员指向了Person函数对象的prototype成员对象?

obj.__proto__ = Person.prototype;

4、判断Person的返回值类型,如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象。

if (typeof(result) == "object")
  person = result;
else
  person = obj;

宏任务和微任务

执行过程

怎样理解setTimeout 执行误差

如果当前 执行栈 所花费的时间大于 定时器 时间,那么定时器的回调在 宏任务(macrotask) 里,来不及去调用,所以这个时间会有误差。

如何实现深浅拷贝

浅拷贝

// 1. ...实现
let copy1 = {...{x:1}}
// 2. Object.assign实现
let copy2 = Object.assign({}, {x:1})
// 数组采用slice、concat方法

深拷贝

// 1. JOSN.stringify()/JSON.parse()
let obj = {a: 1, b: {x: 3}}
JSON.parse(JSON.stringify(obj))

// 2. 递归拷贝
function deepClone(obj) {
  let copy = obj instanceof Array ? [] : {}
  for (let i in obj) {
    if (obj.hasOwnProperty(i)) {
      copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
    }
  }
  return copy
}

JSON.stringify()的缺点

数组去重的几种写法

let list = [1, 2, 3, 4, 5, 2, 2, 2, 7, 6, 4, 8, 1];
// 创建新数组,判断数据是否在新数组中
function arrToRepeat1(arr) {
    let repeatList = [];
    list.forEach(value => {
        if (!repeatList.includes(value)) repeatList.push(value);
    })
    return repeatList;
}
console.log(arrToRepeat1(list));
// 创建新数组,通过下标判断是否是重复的值
function arrToRepeat2(arr) {
    let repeatList = [];
    list.forEach((value, index) => {
        // 左侧的为第一个值的索引,右侧为重复的当前的值的索引
        if (arr.indexOf(value) === index) repeatList.push(value);
    })
    return repeatList;
}
console.log(arrToRepeat2(list));
// 使用set集合
console.log([...new Set(list)]);

数组排序的问题

默认数组排数函数sort有问题,不指定排序方法时,元素会按照转换为的字符串的诸个字符的Unicode位点进行排序

常用解决方法


list.sort((a, b) => a - b) // 从小到大
list.sort((a, b) => b - a) // 从大到小

数组降维

array = array.reduce((acc, val) => acc.concat(val), []);  // 一层扁平化
array = [].concat(...array);  // 一层扁平化
array = array.flat();  // 一层扁平化

手写防抖和节流

js垃圾回收机制,怎么理解js中的内存泄露

垃圾回收机制

垃圾回收是释放已经不再需要的变量等,来减少系统所占用的内存。

标记清除法

工作原理:
当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:

引用计数法

跟踪记录每个值的引用次数(值)
流程:

缺点:循环引用将不能被自动回收。

内存泄漏

内存泄漏是由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费。

引起内存泄漏的情况

DOM事件模型是什么

事件处理的几种写法

移动端的touch事件

事件委托/事件代理

target、currentTarget的区别?

上一篇 下一篇

猜你喜欢

热点阅读