1.快速排序
function sort(start, end, arr){
const pivot = arr[start]
let left = start
let right = end
while(left<right){
while(arr[right] >= pivot && right > left){
right--
}
arr[left] = arr[right]
while(arr[left] < pivot && right > left) {
left++
}
arr[right] = arr[left]
}
arr[left] = pivot
return left
}
function quickSort(start, end , arr) {
if(start < end){
const mid = sort(start, end, arr)
quickSort(start, mid - 1, arr)
quickSort(mid + 1, end, arr)
}
}
2. instanceof
function myInstanceof(target, origin) {
if(typeof target !== "object" || target == null) return false
if(typeof origin !== "function") {
throw new TypeError("origin not func")
}
let proto = Object.getPrototypeOf(target)
while(proto) {
if(proto === origin.prototype) return true
proto = Object.getPrototype(proto)
}
return false
}
3. 数组扁平化
function flat(arr, depth = 1) {
if(depth > 0) {
arr.reduce((pre,cur) => {
return pre.concat(Array.isArray(cur) ? flat(cur, depth - 1) : cur)
},[])
}
return arr.slice()
}
4.手写reduce
//先不考虑第二个参数初始值
Array.prototype.reduce1 = function( fn ) {
const arr = this
let total = arr[0]
for(let i = 1 ; i < arr.length ; i++) {
total = fn(total, arr[i],i,arr)
}
return total
}
Array.prototype.reduce2 = function(fn, init){
const arr = this
let total = init || arr[0]
for(let i = init?0 : 1 ; i < arr.length ; i++){
total = fn(total , arr[i] , i , arr)
}
return total
}
5.数组去重(多种方法)
function unique(arr) {
return [...new Set(arr)]
}
function unique(arr) {
return arr.filter((item,index,array) => {
return array.indexOf(item) === index
})
}
6. new的实现
1.首先创建一个新的空对象
2.根据原型链,设置空对象的 __proto__ 为构造函数的 prototype 。
3.构造函数的this指向这个对象,致性构造函数的代码(为这个新对象添加属性)。
4.判断函数的返回值类型,如果是引用类型,就返回这个引用类型的对象
function myNew(context) {
const obj = new Object()
obj._proto_ = context.prototype
const res = context.apply(obj,[...arguments].slice(1))
return typeof res === "object" ? res : obj
}
7.call,apply,bind的实现
-
call : call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
-
Function.prototype.myCall = function(context) { // 判断调用对象 if(typeof this!= "function") throw new Error("Type error") let args = [..arguments].slice(1) let res= null //判断是否传入了this context = context || window //this就是我们使用的函数,obj.fn()时,fn中的this指向obj,有关this指向请看我的另一篇文章 context.fn = this res = context.fn(...args) //删除属性 delete context.fn return res } -
apply: apply和call的区别在于 app把参数 按照数组的形式传进去了
Function.prototype.myApply = function(context) { // 判断调用对象 if(typeof this!= "function") throw new Error("Type error") let res= null //判断是否传入了this context = context || window //this就是我们使用的函数,obj.fn()时,fn中的this指向obj,有关this指向请看我的另一篇文章 context.fn = this //可以使用Symbol防止同名属性 //const fnSymbol = Symbol(); //context[fnSymbol] = this; if(arguments[1]) { res = context.fn(...arguments[1]) //result = context[fnSymbol](...arguments[1]); }else{ res = context.fn() } //删除属性 delete context.fn return res } -
bind : 返回一个函数 可以继续传递参数
Function.prototype.bind = function (context) { // 调用 bind 的不是函数,需要抛出异常 if (typeof this !== "function") { throw new Error("Function.prototype.bind - what is trying to be bound is not callable"); } // this 指向调用者 var self = this; // 实现第2点,因为第1个参数是指定的this,所以只截取第1个之后的参数 var args = Array.prototype.slice.call(arguments, 1); // 实现第3点,返回一个函数 return function () { // 实现第4点,这时的arguments是指bind返回的函数传入的参数 // 即 return function 的参数 var bindArgs = Array.prototype.slice.call(arguments); // 实现第1点 return self.apply( context, args.concat(bindArgs) ); } }但还有一个问题,
bind有以下一个特性:一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器,提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
来个例子说明下:
let value = 2; let foo = { value: 1 }; function bar(name, age) { this.habit = 'shopping'; console.log(this.value); console.log(name); console.log(age); } bar.prototype.friend = 'kevin'; let bindFoo = bar.bind(foo, 'Jack'); let obj = new bindFoo(20); // undefined // Jack // 20 obj.habit; // shopping obj.friend; // kevin上面例子中,运行结果
this.value输出为undefined,这不是全局value也不是foo对象中的value,这说明bind的this对象失效了,new的实现中生成一个新的对象,这个时候的this指向的是obj。这个可以通过修改返回函数的原型来实现,代码如下:
Function.prototype.bind = function (context) { // 调用 bind 的不是函数,需要抛出异常 if (typeof this !== "function") { throw new Error("Function.prototype.bind - what is trying to be bound is not callable"); } // this 指向调用者 var self = this; // 实现第2点,因为第1个参数是指定的this,所以只截取第1个之后的参数 var args = Array.prototype.slice.call(arguments, 1); // 创建一个空对象 var fNOP = function () {}; // 实现第3点,返回一个函数 var fBound = function () { // 实现第4点,获取 bind 返回函数的参数 var bindArgs = Array.prototype.slice.call(arguments); // 然后同传入参数合并成一个参数数组,并作为 self.apply() 的第二个参数 return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs)); // 注释1 } // 注释2 // 空对象的原型指向绑定函数的原型 fNOP.prototype = this.prototype; // 空对象的实例赋值给 fBound.prototype fBound.prototype = new fNOP(); return fBound; } //简短版 Function.prototype.myBind = function (context) { // 判断调用对象是否为函数 if (typeof this !== "function") { throw new Error("Type error"); } // 获取参数 const args = [...arguments].slice(1), const fn = this; return function Fn() { return fn.apply( this instanceof Fn ? this : context, // 当前的这个 arguments 是指 Fn 的参数 args.concat(...arguments) ); }; };注释1 :
- 当作为构造函数时,
this指向实例,此时this instanceof fBound结果为true,可以让实例获得来自绑定函数的值,即上例中实例会具有habit属性。 - 当作为普通函数时,
this指向window,此时结果为false,将绑定函数的this指向context
注释2 :
- 修改返回函数的
prototype为绑定函数的prototype,实例就可以继承绑定函数的原型中的值,即上例中obj可以获取到bar原型上的friend - 至于为什么使用一个空对象
fNOP作为中介,把fBound.prototype赋值为空对象的实例(原型式继承),这是因为直接fBound.prototype = this.prototype有一个缺点,修改fBound.prototype的时候,也会直接修改this.prototype
- 当作为构造函数时,