91.实现一个 promise
参考链接:实现一个完美符合Promise/A+规范的Promise · Issue #4 · forthealllight/blog · GitHub
function myPromise(constructor) {
let self = this;
self.status = "pending" // 定义状态改变前的初始状态
self.value = undefined;// 定义状态为resolved的时候的状态
self.reason = undefined;// 定义状态为rejected的时候的状态
function resolve(value) {
// 两个 === "pending",保证了状态的改变是不可逆的
if(self.status === "pending") {
self.value = value;
self.status = "resolved";
}
}
function reject(reason) {
// 两个 === "pending",保证了状态的改变是不可逆的
if(self.status === "pending") {
self.reason = reason;
self.status = "rejected";
}
}
// 捕获构造异常
try {
constructor(resolve, reject);
}
catch(e) {
reject(e);
}
}
// 在myPromise的原型上定义链式调用的then方法
myPromise.prototype.then = function(onFullfilled, onRejected) {
let self = this;
switch(self.status) {
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
92.Function.proto(getPrototypeOf)
获取一个对象的原型,在 chrome 中可以通过proto的形式,或者在 ES6 中可以通过 Object.getPrototypeOf 的形式。
Function.proto 说明 Function 由什么对象继承而来:
Function.__proto__ == Object.prototype; // false
Function.__proto__ == Function.prototype; // true
说明 Function 的原型也是 Function
93.数组常用方法
push() 和 unshift():向数组的 尾部/头部 添加若干元素,并返回 数组的 新长度;
var arr = [1, 2];
// 向末尾追加元素
arr.push(3, 4); // arr = [1,2,3,4];
// 向开头追加元素
arr.unshift(6, 7); // arr = [6,7,1,2,3,4];
pop() 和 shift():从数组的 尾部/头部 删且只删1个元素;空数组是继续删除,且不报错,返回undefined
// 删除末尾元素
arr.pop(); // arr = [6,7,1,2,3];
// 删除开头元素
arr.shift(); // arr = [7,1,2,3]
splice() 方法用于添加或删除数组中的元素,会改变原始数组。
slice() 方法可从已有的数组中返回选定的元素,可提取字符串的某个部分,并以新的字符串返回被提取的部分,不会改变原始数组。
94.数组去重
1.indexOf 循环去重:
返回某个指定的元素在数组中首次出现的位置:定义一个空数组res,调用 indexOf() 方法对原来的数组进行遍历,如果元素不在res中,将其 push 进res中,最后将res返回即可获得去重的数组(判断新数组中是否有a[i],如果没有indexOf,返回-1,把a[i]放入新数组中)
var res = []
for (var i = 0; i < arr.length; i++) {
if (res.indexOf(arr[i]) == -1) {
res.push(arr[i]);
}
}
return res;
2.ES6 Set 去重:
var newArr = Array.from(new Set(arr));
3.Object 键值对去重:
把数组的值存成 Object 的 key 值,比如 Object[val1] = true, 在判断另一个值的时候,如果 Object[val2] 存在,说明该值重复
95.去除字符串首尾空格
使用正则(^\s)|(\s$)
96.性能优化
通过 减少 HTTP 请求;使用内容发布网络(CDN);添加本地缓存;压缩资源文件;将 CSS 样式表放在顶部;把 javascript 放在底部(浏览器的运行机制决定);避免使用 CSS 表达式;减少 DNS 查询;使用外部 javascript 和 CSS;避免重定向;图片懒加载等。
97.跨域的原理
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 js 实施的安全限制,只要协议、域名、端口有任一不同,都被当作不同的域。
98.如何解决跨域
1. CORS(跨域资源共享):在服务器端配置CORS,允许特定域名的请求访问您的资源。通过设置适当的响应头,控制跨域请求的访问权限。
2. JSONP:使用JSONP允许从不同域名获取数据。但JSONP只适用于GET请求,可能存在安全风险。
3. 代理服务器:在服务器上设置一个代理,将跨域请求转发到目标服务器,然后将响应返回给客户端。
4. WebSocket:如果需要实时通信,WebSocket是一种可以跨域的双向通信协议。
5. 服务器配置:调整服务器配置,使请求绕过跨域限制(如在Nginx或Apache上设置反向代理)。
6. 使用跨域库:使用JavaScript库(如axios)提供了简化处理跨域请求的方法。
99.暂停死区
在代码块内,使用 let、const 命令声明变量之前(没有变量提升),该变量都是不可用的。
100.游戏《Flappy Bird》
一只小鸟在飞,前面是无尽的沙漠,上下不断有钢管生成,要躲避钢管。在玩这个游戏时总出现卡顿甚至崩溃,其原因以及解决办法:
这是单机游戏,回答与网络无关
1.内存溢出问题:在钢管离开可视区域后,销毁钢管;不断生成钢管且不及时清理,容易内存溢出从而游戏崩溃,因此可通过垃圾收集器回收钢管。
2.资源过大问题:选择图片文件更小的图片格式,比如 webp、png 格式,因为绘制图片需要较大计算量。
3.资源加载问题:在可视区域之前预加载好资源,如果在可视区域生成钢管的话,用户认为钢管是卡顿后才生成的,用户体验不流畅。
4.canvas 绘制频率问题:大部分显示器刷新频率为 60 次/s,因此游戏的每一帧绘制间隔时间需要小于 1000/60=16.7ms,才能实现不卡顿。
101.按需加载
当用户触发了动作时才加载对应的功能。触发的动作要看具体的业务场景,包括但不限于:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更改等。加载的文件可以是 JS、图片、CSS、HTML 等。
102.什么是 virtual dom(虚拟dom)
用 JavaScript 对象结构表示 DOM 树的结构,用这个树构建一个真正的 DOM 树,插到文档中, 当状态变更的时候,重新构造一棵新的对象树。将新树和旧树比较,记录差异,应用到所构建的真正 DOM 树中,从而实现视图更新。Virtual DOM 本质上是在 JS 和 DOM 之间做一个缓存。
103.Promise 回调链
Promise 能够在回调函数里使用 return 和 throw, 所以在 then 中可以 return 出一个 promise 对象或其他值,也可以 throw 出一个错误对象;如果没有 return,将默认返回 undefined,后面的 then 中的回调参数接收到的是 undefined。
104.promise 和 await/async 的关系
"异步" 用于描述在执行代码时不会阻塞程序的其他操作。当一个操作被标记为异步时,程序可以继续执行其他任务,而不必等待该操作完成。
"async" 是在JavaScript中定义异步函数的关键字。异步函数返回一个 Promise 对象,可以使用 "await" 关键字来等待异步操作的完成。
"Promise" 是 JavaScript 中用于处理异步操作的对象。它表示一个可能会在未来完成的值或错误。Promise 有三种状态:等待(pending)、已解决(fulfilled)和已拒绝(rejected)。当 Promise 被解决或拒绝时,它会调用相应的回调函数,允许在异步操作完成后执行相关操作。
以下是一个使用异步函数和 Promise 的示例:
async function fetchData() {
try {
const response = await fetch('https://api.exaPromise.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
fetchData()
.then(data => {
console.log('Fetched data:', data);
})
.catch(error => {
console.error('Error:', error);
});
在上面的示例中,`fetchData` 是一个异步函数,它使用 "await" 关键字来等待异步操作(fetch 请求和 JSON 解析)的完成。该函数返回一个 Promise,使用 `.then()` 来处理解决的 Promise 或使用 `.catch()` 来处理拒绝的 Promise。
105.ES6,ES7 的语法
ES6 语法特性:
1. 箭头函数:简化了函数的书写,同时固定了函数内部的上下文。
2. let 和 const 声明:引入了块级作用域的变量声明,取代了之前的 var 声明。
3. 解构赋值:可以从对象或数组中提取值,并将它们赋值给变量。
4. 模板字符串:使用反引号(`)创建字符串,可以在其中插入变量或表达式。
5. 类:引入了基于原型的面向对象编程的类语法。
6. Promise:引入了用于处理异步操作的 Promise 对象。
7. 默认参数:在函数声明中可以为参数设置默认值。
8. 展开操作符(拓展运算符):用于在数组或对象字面量中展开数组或对象。
9. 迭代器和生成器:提供了更灵活的迭代和异步编程模型。
ES7 语法特性:
1. 指数操作符:使用双星号(**)进行指数运算。
2. Array.prototype.includes() 方法:用于判断数组中是否包含某个元素。
3. async/await:用于更简洁地处理异步操作,使异步代码看起来更像同步代码。
4. Object.values() 和 Object.entries() 方法:用于获取对象的值数组和键值对数组。
5. String.prototype.padStart() 和 String.prototype.padEnd() 方法:用于填充字符串到指定长度。