共同点
bind,apply和call都可以改变使用对象的this指向
不同点
1.bind绑定不会立即执行,需要手动调用
2.apply,call绑定函数会立即执行,如果传入的第一个参数是null,undefined则this指向window。apply和call的区别是:apply第二个参数是一个数组,而call从第二个参数起后面传入的都是单个的参数。
手写apply,call,bind
1.手写call
Function.prototype.myCall = function(context){
context = context || window
context.fn = this
let args = [...arguments]
args.shift()
const res = context.fn(...args)
delete context.fn
return res
}
//测试代码
function Apple(){}
Apple.prototype = {
color: 'red',
say: function(){
console.log(`this.color is ${this.color}`)
}
}
const banana = {
color: 'yellow'
}
const apple = new Apple();
apple.say.myCall(banana) // this.color is yellow
// 传入null ,this就指向window, window.color没有值,返回undefined
apple.say.myCall(null) // this.color is undefined
2.手写apply
Function.prototype.myApply = function(context,arr){
context = context || window
context.fn = this
let res = null
if(!arr){
res = context.fn()
}
let args = [...arr]
res = context.fn(...args)
delete context.fn
return res
}
//测试代码
function Apple(){}
Apple.prototype = {
color: 'red',
say: function(a,b){
console.log(`this.color is ${this.color}`)
console.log('a',a,'b',b)
}
}
const banana = {
color: 'yellow'
}
const apple = new Apple();
apple.say.myApply(banana,[1,2])
// this.color is yellow
// a 1 b 2
// 传入null ,this就指向window, window.color没有值,返回undefined
apple.say.myApply(banana,null) // this.color is yellow
// a undefined b undefined
3.手写bind
第一种:借助apply
Function.prototype.myBind = function(){
const _this = this
// 获取第一个参数,要指向的this对象
const firstArg = Array.prototype.shift.call(arguments)
// 获取剩余的参数
const args = Array.prototype.slice.call(arguments)
return function(){
// 获取内部函数的所有参数
const innerArg = Array.prototype.slice.call(arguments)
const lastArg = args.concat(innerArg)
_this.apply(firstArg,lastArg)
}
}
第二种:不借助apply
Function.prototype.myBind = function(){
const _this = this
const context = Array.prototype.shift.call(arguments)
const args = Array.prototype.slice.call(arguments)
return function(){
const innerArg = Array.prototype.slice.call(arguments)
const lastArg = args.concat(innerArg)
context.fn = _this
const resut = context.fn(...lastArg)
delete context.fn
return resut
}
}
4.实现一个new关键字
function Person(){}
function newFun(name,age){
const newObject = new Object()
Person.call(newObject,name,age)
newObject.__proto__ = Person.prototype
return newObject
}
手写节流和防抖
节流
//节流 时间戳
function throttle(fn,delay){
let pre = 0
return function(){
const context = arguments
const _this = this
const now = +Date.now()
if(now - pre > delay){
fn.apply(_this,context)
pre = now
}
}
}
// 节流 定时器
function throttle(fn,delay){
let timer = null
return function(){
const context = arguments
const _this = this
if(!timer){
timer = setTimeout(()=>{
fn.apply(_this,context)
timer = null
},delay)
}
}
}
防抖
function debounce(fn,delay){
let timer = null
return function(){
const args = arguments
const _this = this
clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(_this,args)
timer = null
},delay)
}
}
排序算法
1.冒泡
function bubbleSort1(arr) {
if (arr.length < 2) {
return arr;
}
// 定义 count 代表执行了趟循环
let count = 0;
// 冒泡排序排的趟数=数组的长度-1
// 第一层 for 循环代表一共排序多少趟
for (let i = 0; i < arr.length - 1; i++) {
count++;
let hasSort = true;
// 第二层 for 循环代表每趟需要比较多少次 每趟比较的次数会减i,因为每趟会将最后排好一个元素,排了多少趟,则代表排好了几个元素
for (let j = 0; j < arr.length - 1 - i; j++) {
// 在大于的时候才会交换,等于的时候不交换,所以冒泡排序属于稳定排序算法,不会对相等两个元素执行交换
// j=>左指针,j + 1=>右指针
if (arr[j] > arr[j + 1]) {
// 只要交换过,则不是有序
hasSort = false;
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (hasSort) {
break;
}
}
console.log(`执行了${count}趟循环`);
return arr;
}
2.快排
function qucikSort1(arr){
if(arr.length <= 1) return arr
const pre = arr.splice(0,1)
let left = [],right = []
arr.forEach(item=>{
if(item < pre){
left.push(item)
}else {
right.push(item)
}
})
return qucikSort1(left).concat(pre).concat(qucikSort1(right))
}
qucikSort1([2,4,1,6]) // [1, 2, 4, 6]