0%

js高频考题


整理了一下常见的js的问题

1~10

JavaScript 有几种类型的值? 你能简单说一下他们的区别吗?

  • 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
  • 堆:引用数据类型(对象、数组和函数)

两种类型间的主要区别是它们的存储位置不同,基本数据类型的值直接保存在栈中,而复杂数据类型的值保存在堆中,通过使用在栈中保存对应的指针来获取堆中的值。

JavaScript 原型,原型链? 有什么特点?

戳我跳转

js 获取原型的方法?

  • p.proto
  • p.constructor.prototype
  • Object.getPrototypeOf(p)

在 js 中不同进制数字的表示方式

  • 以 0X、0x 开头的表示为十六进制。
  • 以 0、0O、0o 开头的表示为八进制。
  • 以 0B、0b 开头的表示为二进制格式。

typeof NaN 的结果是什么?

NaN 意指“不是一个数字”(not a number),NaN 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。

typeof NaN; // “number”

NaN 是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 x === x 不成立)的值。而 NaN != NaN 为 true。

javascript 创建对象的几种方式?

我们一般使用字面量的形式直接创建对象,但是这种创建方式对于创建大量相似对象的时候,会产生大量的重复代码。

  • 工厂模式

工厂模式的主要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目的。但是它有一个很大的问题就是创建出来的对象无法和某个类型联系起来,它只是简单的封装了复用代码,而没有建立起对象和类型间的关系。

  • 构造函数模式

只要一个函数是通过 new来调用的,那么我们就可以把它称为构造函数。执行构造函数首先会创建一个对象,然后将对象的原型指向构造函数的 prototype 属性,然后将执行上下文中的 this 指向这个对象,最后再执行整个函数,如果返回值不是对象,则返回新建的对象。因为 this 的值指向了新建的对象,因此我们可以使用 this 给对象赋值。

构造函数模式相对于工厂模式的优点是,所创建的对象和构造函数建立起了联系,因此我们可以通过原型来识别对象的类型

  • 原型模式

因为每一个函数都有一个 prototype 属性,这个属性是一个对象,它包含了通过构造函数创建的所有实例都能共享的属性和方法。因此我们可以使用原型对象来添加公用属性和方法,从而实现代码的复用

JavaScript 继承的几种实现方式

戳我跳转

谈谈 This 对象的理解。什么是闭包,说一下闭包。

戳我跳转

[“1”, “2”, “3”].map(parseInt) 答案是多少?

parseInt() 函数能解析一个字符串,并返回一个整数,需要两个参数 (string, radix)

其中radix 表示要解析的数字的基数。(该值介于 2 ~ 36 之间)

如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。

由于map传参默认有三个(currentValue, index, arr)

所以传入到parseInt中去之后,就变成了下面这样了,后面的0,1,2 对应的就是index索引

1
2
3
parseInt('1', 0);
parseInt('2', 1);
parseInt('3', 2);

当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。

所以答案是 [1, NaN, NaN]

如何判断一个对象是否属于某个类?

  1. 使用 instanceof 运算符来判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
  2. 可以通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写。
  3. 如果需要判断的是某个内置的引用类型的话,可以使用Object.prototype.toString() 方法来打印对象的[[Class]] 属性来进行判断。

11~20

new 操作符具体干了什么呢?如何实现?

戳我跳转

一行代码学习js

1
[].forEach.call($$("*"),function(a){a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)})

解释一下上面这段代码的意思

  1. 选取页面所有 DOM 元素。在浏览器的控制台中可以使用$$()方法来获取页面中相应的元素,这是现代浏览器提供的一个命令行 API 相当于 document.querySelectorAll 方法。
  2. 循环遍历 DOM 元素
  3. 给元素添加 outline 。由于渲染的 outline 是不在 CSS 盒模型中的,所以为元素添加outline 并不会影响元素的大小和页面的布局。
  4. 生成随机颜色函数。Math.random()*(1<<24) 可以得到 0~2^24 - 1 之间的随机数,因为得到的是一个浮点数,但我们只需要整数部分,使用取反操作符 ~ 连续两次取反获得整数部分,然后再用 toString(16) 的方式,转换为一个十六进制的字符串。

如何创建一个 Ajax

  1. 创建 XMLHttpRequest 对象,也就是创建一个异步调用对象
  2. 创建一个新的 HTTP 请求,并指定该 HTTP 请求的方法、URL 及验证信息
  3. 设置响应 HTTP 请求状态变化的函数
  4. 发送 HTTP 请求
  5. 获取异步调用返回的数据
  6. 使用 JavaScript 和 DOM 实现局部刷新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创建 Http 请求
xhr.open("GET", SERVER_URL, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功时
if (this.status === 200) {
handle(this.response);
} else {
console.error(this.statusText);
}
};
// 设置请求失败时的监听函数
xhr.onerror = function() {
console.error(this.statusText);
};
// 设置请求头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 请求
xhr.send(null);
// promise 封装实现:
function getJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}

JS事件循环机制

戳我跳转

什么是浏览器的同源政策?如何解决跨域问题?

戳我跳转

戳我跳转

简单介绍几种模块规范

  • CommonJS 方案

它通过 require 来引入模块,通过 module.exports 定义模块的输出接口

  • AMD 方案

采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。

  • CMD 方案

这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和 require.js 的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。

  • ES6 模块化

使用 import 和 export 的形式来导入导出模块。

AMD 和 CMD 规范的区别?

  • 在模块定义时对依赖的处理不同

AMD 推崇依赖前置,在定义模块的时候就要声明其依赖的模块。
CMD 推崇 就近依赖,只有在用到某个模块的时候再去 require。

  • 对依赖模块的执行时机处理不同。

首先 AMD 和 CMD 对于模块的加载方式都是异步加载,不过它们的区别在于 模块的执行时机

AMD 在依赖模块加载完成后就直接执行依赖模块,依赖模块的执行顺序和我们书写的顺序不一定一致
CMD 在依赖模块加载完成后并不执行,只是下载而已,等到所有的依赖模块都加载好后,进入回调函数逻辑,遇到 require 语句 的时候才执行对应的模块,这样模块的执行顺序就和我们书写的顺序保持一致了。

DOM 操作——怎样添加、移除、移动、复制、创建和查找节点?

  • 创建新节点

    • createDocumentFragment(node);
    • createElement(node);
    • createTextNode(text);
  • 添加、移除、替换、插入

    • appendChild(node)
    • removeChild(node)
    • replaceChild(new,old)
    • insertBefore(new,old)
  • 查找

    • getElementById();
    • getElementsByName();
    • getElementsByTagName();
    • getElementsByClassName();
    • querySelector();
    • querySelectorAll();
  • 属性操作

    • getAttribute(key);
    • setAttribute(key,value);
    • hasAttribute(key);
    • removeAttribute(key);

.call() 和 .apply() 的区别?

戳我跳转

[,,,] 的长度?

尾后逗号 (有时叫做“终止逗号”)在向 JavaScript 代码添加元素、参数、属性时十分有用。如果你想要添加新的属性,并且上一行已经使用了尾后逗号,你可以仅仅添加新的一行,而不需要修改上一行。这使得版本控制更加清晰,以及代码维护麻烦更少。

如果使用了多于一个尾后逗号,会产生间隙。 带有间隙的数组叫做稀疏数组(密致数组没有间隙)。稀疏数组的长度为逗号的数量。

21~30

. JavaScript 中的作用域与变量声明提升?

戳我跳转

深入理解垃圾回收原理

戳我跳转

哪些操作会造成内存泄漏?

戳我跳转

移动端为什么会有点击延迟,怎么解决

戳我跳转

使用 JS 实现获取文件扩展名?

1
2
3
4
5
6
7
8
9
// String.lastIndexOf() 方法返回指定值(本例中的'.')在调用该方法的字符串中最后出现的位置,如果没找到则返回 -1。

// 对于 'filename' 和 '.hiddenfile' ,lastIndexOf 的返回值分别为 0 和 -1 无符号右移操作符(>>>) 将 -1 转换为 4294967295 ,将 -2 转换为 4294967294 ,这个方法可以保证边缘情况时文件名不变。

// String.prototype.slice() 从上面计算的索引处提取文件的扩展名。如果索引比文件名的长度大,结果为""。

function getFileExtension(filename) {
return filename.slice(((filename.lastIndexOf(".") - 1) >>> 0) + 2);
}

介绍一下 js 的节流与防抖

戳我跳转

Object.is() 与原来的比较操作符 “===”、“==” 的区别?

戳我跳转

js 的事件循环是什么?

戳我跳转

js 中的深浅拷贝实现?

浅拷贝的实现;

1
2
3
4
5
6
7
8
9
10
11
12
13
14

function shallowCopy(object) {
// 只拷贝对象
if (!object || typeof object !== "object") return;
// 根据 object 的类型判断是新建一个数组还是对象
let newObject = Array.isArray(object) ? [] : {};
// 遍历 object,并且判断是 object 的属性才拷贝
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] = object[key];
}
}
return newObject;
}

深拷贝的实现

1
2
3
4
5
6
7
8
9
10
11
function deepCopy(object) {
if (!object || typeof object !== "object") return;
let newObject = Array.isArray(object) ? [] : {};
for (let key in object) {
if (object.hasOwnProperty(key)) {
newObject[key] =
typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
}
}
return newObject;
}

函数柯里化的实现

戳我戳我

31~40

为什么 0.1 + 0.2 != 0.3?如何解决这个问题?

戳我跳转

前端安全

戳我跳转

offsetWidth/offsetHeight,clientWidth/clientHeight 与 scrollWidth/scrollHeight 的区别?

clientWidth/clientHeight 返回的是元素的内部宽度,它的值只包含 content + padding,如果有滚动条,不包含滚动条。
clientTop 返回的是上边框的宽度。clientLeft 返回的左边框的宽度。

offsetWidth/offsetHeight 返回的是元素的布局宽度,它的值包含 content + padding + border 包含了滚动条。
offsetTop 返回的是当前元素相对于其 offsetParent 元素的顶部的距离。offsetLeft 返回的是当前元素相对于其 offsetParent 元素的左部的距离。

scrollWidth/scrollHeight 返回值包含 content + padding + 溢出内容的尺寸。
scrollTop 属性返回的是一个元素的内容垂直滚动的像素数。scrollLeft 属性返回的是元素滚动条到元素左边的距离。

什么是 Promise 对象?

戳我戳我

手写promise

戳我戳我

js 设计模式

  • 单例模式

单例模式保证了全局只有一个实例来被访问。比如说常用的如弹框组件的实现和全局状态的实现。

  • 策略模式

策略模式主要是用来将方法的实现和方法的调用分离开,外部通过不同的参数可以调用不同的策略。

  • 代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。比如说常见的事件代理。

  • 中介者模式

中介者模式指的是,多个对象通过一个中介者进行交流,而不是直接进行交流,这样能够将通信的各个对象解耦。

  • 适配器模式

适配器用来解决两个接口不兼容的情况,不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。

  • 观察者模式

发布订阅模式其实属于广义上的观察者模式, 在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。

  • 发布订阅模式

而在发布订阅模式中,发布者和订阅者之间多了一个调度中心。调度中心一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要在调度中心中订阅事件。通过调度中心实现了发布者和订阅者关系的解耦

更详细的可以看看我朋友的博客戳我跳转

开发中常用的几种 Content-Type ?

  • application/x-www-form-urlencoded

浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以
application/x-www-form-urlencoded 方式提交数据。该种方式提交的数据放在 body 里面,
数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL转码。

  • multipart/form-data

该种方式也是一个常见的 POST 提交方式,通常表单上传文件时使用该种方式。

  • application/json

告诉服务器消息主体是序列化后的 JSON 字符串。

  • text/xml

该种方式主要用来提交 XML 格式的数据。

如何封装一个 javascript 的类型判断函数?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getType(value) {
// 判断数据是 null 的情况
if (value === null) {
return value + "";
}
// 判断数据是引用类型的情况
if (typeof value === "object") {
let valueClass = Object.prototype.toString.call(value),
type = valueClass.split(" ")[1].split("");
type.pop();
return type.join("").toLowerCase();
} else {
// 判断数据是基本数据类型的情况和函数的情况
return typeof value;
}
}

(未完待续…)

-------------本文结束感谢您的阅读-------------
没办法,总要恰饭的嘛~~