手写题

数组去重

冒泡排序

详情
// 将数组中的数从小到大排列
var arr = [1, 4, 6, 7, 9, 3, 5, 8]
// var numi = 0
// var numj = 0
for (var j = 0; j < arr.length - 1; j++) {
// numj += 1
var flag = true
for (var i = 0; i < arr.length - 1 - j; i++) {
// document.write('(' + arr[i] + ',' + arr[i + 1] + ')')
// numi += 1
// 两两比较,如果前面的大于后面的,交换位置
if (arr[i] > arr[i + 1]) {
flag = false
var temp
temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
// document.write('交换了')
}
}
// document.write(',arr=(' + arr + ')')
// document.write('<br>')
// 如果一趟下来,一次交换都没有做,说明就已经排好序,就不需要继续比
if (flag) {
break
}
}

实现 call apply bind 方法

详情

call 和 apply 区别

call:

Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
var args = Array.prototype.slice.apply(arguments, [1]) // 获取额外参数
// var args = [...arguments].slice(1)
context.fn = this
var res = context.fn(...args)
delete context.fn
return res
}

context 为要绑定的 this,不传默认为 window
给 context 创建一个 fn 属性,并将值设置为需要调用的函数
调用 context.fn,并将额外参数 args 传递进去
删除 context 上的 fn 函数

apply:

Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let res
if (!arguments[1]) {
res = context.fn()
} else if (arguments[1].constructor.name === 'Array') {
res = context.fn(...arguments[1])
} else {
return console.error('Uncaught TypeError: CreateListFromArrayLike called on non-object')
// throw 'Uncaught TypeError: CreateListFromArrayLike called on non-object'
}
delete context.fn
return res
}

bind:

Function.prototype.myBind = function() {
var self = this // 保存原函数
var args = Array.prototype.slice.call(arguments) // 参数转为数组
// var args = [...arguments].slice(1) // 参数转为数组
var context = args.shift() // 保存需要绑定的this上下文
return function() {
// 返回一个新函数
self.apply(context, args.concat([].slice.call(arguments)))
}
}
function aaa(val, val1) {
console.log(val)
console.log(val1)
console.log(this.name)
}
aaa()
aaa.myCall({ name: '123' }, '121', 122)
aaa.myApply({ name: '123' }, ['121', 122])
aaa.myBind({ name: '123' })('111', '222')

防抖和节流

深浅拷贝

手写 Promise

手写 Promise.all

手写 Promise.race

异步调度

实现继承的方式

参考:https://www.cnblogs.com/humin/p/4556820.html

原型链继承,将父类的实例作为子类的原型,无法实现多继承

function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

构造继承,使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}

组合继承,通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Cat(name){
Animal.call(this);
this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;

调用了两次父类构造函数,生成了两份实例

寄生组合继承
通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

es6 中的继承

class Super {}
class Sub extends Super {}

const sub = new Sub()

Sub.__proto__ === Super

子类可以直接通过 __proto__ 寻址到父类

function Super() {}
function Sub() {}

Sub.prototype = new Super()
Sub.prototype.constructor = Sub

var sub = new Sub()

Sub.__proto__ === Function.prototype

而通过 ES5 的方式,Sub.__proto__ === Function.prototype

es5继承 与 es6 继承的区别参考 https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/20

发布订阅

扁平数据转tree

实现 instanceof

// Left-hand instanceof Right-hand Right-hand.prototype 是否在 Left-hand 的原型链中
function myInstanceof(left, right) {
if (typeof left === 'undefined' || left === null) return false

// getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left)
while (true) {
// 查找到尽头,还没找到
if (proto == null) return false
// 找到相同的原型对象
if (proto == right.prototype) return true
proto = Object.getPrototypeof(proto)
}
}

实现一个 new 操作符

实现 JSON.stringify

手机号 3-4-4 分割
千分位格式化数字