new
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
// 使用 __proto__ 是有争议的,也不鼓励使用它。
// 因为它从来没有被包括在 EcmaScript 语言规范中,但是现代浏览器都实现了它。
function _new() {
// Constructor
let newObj = null,
result = null,
Constructor = Array.prototype.shift.call(arguments);
/**
* Constructor = Array.prototype.shift.call(arguments);
* 删除 arguments 的第一个选项并返回,arguments 的长度被改变
*/
// 1 创建一个空对象
// 2 设置对象的原型为构造函数的原型对象
// 3 执行构造函数,把 this 指向新建的对象
// 4 返回新对象
newObj = new Object();
newObj.__proto__ = Constructor.prototype;
Constructor.apply(newObj, arguments)
return newObj;
// 优化
// newObj = Object.create(Constructor.prototype)
// Constructor.apply(newObj, arguments)
// return newObj;
}
// 测试代码
function Person() {
this.name = 'zenghp'
this.options = Array.from(arguments)
}
Person.prototype.getName = function(){
return this.name;
}
let person = _new(Person, '123', '123')
console.log(person.getName(), person.name, person.options)
instanceof
// 判断构造函数的原型是否在实例的原型链上
function _instanceOf(a, b) {
// Object.getPrototypeOf(a) === a.__proto__
let aProto = Object.getPrototypeOf(a)
const bProto = b.prototype
while(aProto) {
if(aProto === bProto) return true;
aProto = Object.getPrototypeOf(aProto);
}
}
call、apply、bind
promise promise.all promise.rece
实现类的继承 extend
map
reduce
千分符
深浅拷贝
// https://github.com/febobo/web-interview/issues/56
// 浅拷贝:创建一个对象,这个对象有着原始对象属性值的一份精确拷贝,基本数据类型拷贝的是值,引用数据类型拷贝的是引用地址
// 深拷贝:和浅拷贝类似,唯一不同是对象会重新开辟一个新的栈,和原来的的对象是两个不同的引用地址,修改一个对象的属性不会影响另一个对象
// 测试用例
const obj = {
age: 18,
nature: ['smart', 'good'],
names: {
name1: 'fx',
name2: 'xka'
},
reg: new RegExp(/\d+/),
love: function () {
console.log('fx is a great girl')
},
nickname: undefined,
unique: Symbol('#unique')
}
// 浅拷贝
function clone(obj) {
const newObj = {}
for(let prop in obj) {
// 判断属性是否在对象的属性中 返回布尔值
// if(!obj.hasOwnProperty(prop)) return;
newObj[prop] = obj[prop]
}
return newObj
}
// 合并源对象到目标对象,返回目标对象。 浅拷贝
// Object.assign(target, ...sources)
const newObj = Object.assign({}, obj)
// 数组浅拷贝,由begin和end决定提取数组元素的索引开始和结束的位置
// Array.prototype.slice([begin[, end]])
const newAry = ary.slice()
// 合并多个数组,返回新数组。浅拷贝
// Array.prototype.concat
const newAry = [].concat(ary)
// 拓展运算符
const newAry = [...fxArr]
// 深拷贝,简化版
// 复杂版:请参考上面 github
function deepClone(obj) {
let newObj = Array.isArray(obj) ? [] : {}
function _isObj(type) {
return type !== null && typeof type === 'object';
}
if(obj && _isObj(obj)) {
for(key in obj) {
if(obj.hasOwnProperty(key)) {
const cur = obj[key]
newObj[key] = cur && _isObj(cur) ? deepClone(cur) : cur
}
}
}
return newObj;
}
// 深拷贝,这种方式存在弊端,会忽略 Undefined Symbol Function, 正则会被拷贝成空对象!!
const newObj = JSON.parse(JSON.stringify(obj));
节流防抖
// 节流:在规定时间内只执行一次,如果在规定时间内重复触发,只会执行一次。(每隔一段时间执行一次)
// 防抖:在规定的时间内如果被重复触发,则时间重新计算。(只会执行一次)
// 想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应。
// 假设电梯有两种运行策略 debounce 和 throttle,超时设定为15秒,不考虑容量限制
// 电梯在15秒内会准时关上门,然后开始运送。 -- 节流。
// 如果在这过程中,有人触发了开门键或者挡住了门,则重新计算,直到15秒后关门开始运送。-- 防抖
// 节流:每个时间段执行一次
function throttle(fn, wait = 500) {
let timer
return function() {
if(timer) return
const _self = this
const _arg = arguments
timer = setTimeout(function(){
timer = null
fn.apply(_self, _arg)
}, wait)
}
}
// 防抖 只执行一次
function debounce(fn, wait = 500) {
let timer
return function(){
// 触发回调,清除重新计算
if(timer) clearTimeout(timer)
const _self = this;
const _arg = arguments
timer = setTimeout(function(){
fn.apply(_self, _arg)
}, wait)
}
}
// 测试用例
function handle(arg) {
console.log(this, arg)
}
window.addEventListener('scroll', debounce(handle, 2000));
// 应用场景
//
// 防抖
// 搜索框搜索输入。只需用户最后一次输入完,再发送请求
// 手机号、邮箱验证输入检测
// 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
//
// 节流
// 滚动加载,加载更多或滚到底部监听
// 搜索框,搜索联想功能
ajax
// Promise 封装
function _ajax(url) {
let promise = new Promise(function(resolve, reject) {
// new XMLHttpRequest 创建实例
// 实例方法 open 初始化请求,设置请求方式、路径、是否异步操作(默认 true)
// 实例方法 onreadystatechange 设置监听函数
// 在回调函数中判断是否下载完成(readyState === 4)
// 如果下载完成则判断是否是 200 状态码,(2开头的都是成功响应)
// 最后 send 发送请求。
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
// xhr 状态码:
// 0 ONSEND 代理被创建,但尚未调用 open() 方法。
// 1 OPENED open() 方法已经被调用。
// 2 HEADERS_RECEIVED send() 方法已经被调用,并且头部和状态已经可获得。
// 3 LOADING 下载中; responseText 属性已经包含部分数据。
// 4 DONE 下载操作已完成。
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;
}
数组去重
// 利用数组去重
let ary = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null,null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
// Array.prototype.from
function unique(ary){
// 返回 Set 唯一值的集合,用 Array.from 转成数组
return Array.from(new Set(ary))
}
// Array.prototype.indexOf
// 返回数组中指定成员的索引,如果不存在则返回 -1
// 对象和 NaN 没有过滤
function unique(ary) {
let newAry = []
ary.forEach(item => {
if(newAry.indexOf(item) !== -1) return
newAry.push(item)
})
return newAry;
}
// Array.prototype.includes
// 判断数组是否包含一个指定的值
// 对象并没有去重
function unique(ary) {
let newAry = []
ary.forEach(item => {
if(newAry.includes(item)) return
newAry.push(item)
})
return newAry;
}
// Array.prototype.filter
function unique(ary) {
return ary.filter(function(item, index){
// 判断当前数组选项的索引位置是否等于当前索引
// indexOf 第二参数是开始查找的位置
return ary.indexOf(item, 0) === index;
})
}
// 解构
function unique(ary) {
return [...new Set(ary)]
}
// Array.prototype.reduce
function unique(ary) {
// reduce 传入两个参数
return ary.reduce((prev, cur) => {
return prev.includes(cur) ? prev : [...prev, cur]
}, [])
// 简化版
// return ary.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], [])
}
数组扁平化
扁平化:将嵌套多层的数据转换成只嵌套一层
let ary = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [14]]], 10]
// Array.prototype.flat([depth]) depth: 提取嵌套的结构深度
// 使用 Infinity,可展开任意深度的嵌套数组
ary.flat(Infinity)
// 递归
function _flat(ary) {
let result = []
for (let i = 0; len = ary.length; i++) {
if(Array.isArray(ary[i])) {
// 递归调用
// result = [...result, flatten(ary[i]]
result = result.concat(_flat(ary[i]))
} else {
result.push(ary[i])
}
}
return result;
}
function _flat(ary) {
// map 遍历转成数字 ( +item 是转换成数字, 不是相加或拼接)
return ary.toString().split(',').map(item => +item);
}
// 逻辑和递归一致 只是换种写法
// 注意 reduce 需要传入第二参数
function _flat(ary) {
return ary.reduce(function(prev, cur) {
return prev.concat(Array.isArray(cur) ? flatten(cur) : cur)
}, [])
}