最后亿遍js总结

原型原型链

继承

call,apply,bind用法及实现

  • 作用:简单来说就是改变this的指向,在一个对象中调用另一个对象的方法

用法

  • applycall的用法相似,会直接执行函数
A.sayName.call(B,'tom')
A.sayName.apply(B,['tom'])
  • bind用法与call相似,不同的是,她不会立即执行,他会返回原函数的拷贝
let bSay = A.sayName.bind(B,'tom') //拷贝
bSay()//调用

实现

//A.myCall(B,arg1,arg2)

//挂载到Function 的原型对象上,以便继承给函数对象调用
Function.prototype.myCall = function (originFun, ...args) {
	//A点出的myCall,这里this 指向A函数
    console.log(this)
    //在B里面定义一个私有属性指向A函数
    originFun.__thisFn__ = this
    //B调用A函数,this指向B
    let result = originFun.__thisFn__(...args) 
    //删除无用属性
    delete originFun.__thisFn__
    return result
}


判断类型的方法

  • typeofinstanceof

  • Object.prototype.toString.call();每个对象都存在toString()方法,默认情况下,被每个Object对象继承,如果未被覆盖,会返回[object type],type是对象的类型。比如:

    let obj= new Object()
    obj.toString() //输出 "[object Object]"
    

    但是对于其他情况

    var arr =new Array(1,2)
    arr.toString() // 输出 "1,2"
    

    这是因为所谓的ArrayString等类型在继承于基类Object时,重写了toString方法,在调用的时候,通过原型链向上查找方法时,找到Array上面的toString就停止查找,直接调用了,所以输出与预想的有偏差,那如何让arr去调用Object上面的该方法呢?显而易见借用call()

    Object.prototype.toString.call(arr) // 输出 "[object Array]"
    

    然后在利用 slice方法截取出来类型

    // 是否字符串
    export const isString = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'String'
    
    // 是否数字
    export const isNumber = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Number'
    
    // 是否boolean
    export const isBoolean = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Boolean'
    
    // 是否函数
    export const isFunction = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Function'
    
    // 是否为null
    export const isNull = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Null'
    
    // 是否undefined
    export const isUndefined = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Undefined'
    
    // 是否对象
    export const isObj = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Object'
    
    // 是否数组
    export const isArray = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Array'
    
    // 是否时间
    export const isDate = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Date'
    
    // 是否正则
    export const isRegExp = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'RegExp'
    
    // 是否错误对象
    export const isError = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Error'
    
    // 是否Symbol函数
    export const isSymbol = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Symbol'
    
    // 是否Promise对象
    export const isPromise = (o) => Object.prototype.toString.call(o).slice(8, -1) === 'Promise'
    

捕获与冒泡

  • 事件捕获与冒泡过程

最后亿遍js总结

捕获

  • 事件捕获阶段,会由外到内,一层一层的检查是否注册了事件

  • 如何在捕获阶段注册事件?

    • 使用 addEventListener注册事件,第三个参数代表是否在捕获阶段处理事件,设置为true
    <div class="box">
        <button class="btn">按钮</button>
    </div>
    
    <script>
        //先显示div 后显示btn
        const box = document.querySelector('.box')
        box.addEventListener(
            'click',
            () => {
                alert('div被点击了')
            },
            true
        )
        const btn = document.querySelector('.btn')
        btn.addEventListener(
            'click',
            () => {
                alert('button被点击了')
            },
            true
        )
    </script>
    
    

冒泡

  • 与捕获相反,在事件冒泡阶段,会从里到外来检查是否注册事件

  • 默认情况下,所有的事件都是在冒泡阶段进行注册,如何阻止事件冒泡?

    • w3标准

      event.stopPropagation()
      
    • IE浏览器

      event.cancelBubble=true
      

事件委托

  • 多个元素注册了相同的事件,可以把这些事件委托到它的父元素上,比较常见的是ul里面的li标签点击事件
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<script>
    const ul = document.querySelector('ul')
    ul.addEventListener('click', (e) => {
        if (e.target.nodeName == 'LI') {
            alert(e.target.innerText)
        }
    })
</script>

闭包问题

  • 在一个内层函数中访问到外层函数的作用域

隔离作用域

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

// Counter1与Counter2互不影响,在闭包内修改变量,不会影响到另一个闭包中的变量
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */

经典for循环闭包问题

for (var i = 1, arr = []; i < 5; i++) {
    setTimeout(() => {
        console.log(i)
    }, 500)
}// 5,5,5,5

//使用闭包

for (var i = 1, arr = []; i < 5; i++) {
    ;(function (i) {
        setTimeout(() => {
            console.log(i)
        }, 1000 * i)
    })(i)
}//1,2,3,4

new对象时发生了什么,实现一个new

const myNew = (constructorFn, ...args) => {
  // 创建一个新的对象
  let obj = {}
  // 创建一个私有属性,指向构造函数的原型对象
  obj.__proto__ = constructorFn.prototype
  // 执行构造函数
  constructorFn.call(obj, ...args)
  //返回这个对象
  return obj
}
function Person(name, age) {
  this.name = name
  this.age = age
}

const Per1 = new Person('Tom', 12)
console.log('Per1', Per1)
const Per2 = myNew(Person, 'Tom', 12)
console.log('Per2',Per2)

最后亿遍js总结

防抖与节流

闭包

深浅拷贝

  • 基本类型 StringNumber等都是按值访问的,它的值存储在栈中
  • 引用类型都是 按引用访问的,它将引用地址存储在栈上,然后再去堆上开辟空间存放他们的值

浅拷贝

  • 如果是基本类型,就直接拷贝基本类型的值,如果是引用类型,就拷贝引用类型的引用地址
  • 浅拷贝对于基本类型来说,他们之间改变值不会相互影响,但是对于引用类型来说,由于拷贝的是地址,这些地址最终都指向同一个堆里面,所以会互相影响

实现方式

  • ES6展开运算符
let obj1 ={name:'tom',age:12,addr:{lng:26.0,lag:45.0},friends:['john','jerry']}
let obj2={...obj1}
obj2.name='jerry'
obj2.frinds[0]='tom'

最后亿遍js总结

ES6展开运算符对于不同结构的数据,存在不同的表现.对于一维的对象或者数组,进行的深拷贝,对于多维的对象或者数组进行的是浅拷贝

  • Object.assign()
let obj1 ={name:'tom',age:12,addr:{lng:26.0,lag:45.0},friends:['john','jerry']}
let obj2= Object.assign({}, obj1);

与解构赋值的作用一样

  • lodash.clone()
var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);

深拷贝

  • 主要针对于引用类型,他会在对上面重新开辟一空间存放对象,这样两个深拷贝的对象就不会互相影响了

实现方式

  • JSON.parse(JSON.stringfy() )
let obj1 ={name:'tom',friends:['jerry','john']}
let obj2 =JSON.parse(JSON.stringify(obj1))
obj2.name='jerry'
obj2.friends[0]='tom'

最后亿遍js总结

  • lodash.cloneDeep()
var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
  • 递归遍历实现
function clone(targetObj) {
    //判断是否未对象,是对象去遍历拷贝
    if (typeof targetObj === 'object') {
        //判断源数据是对象还是数组
        let cloneTarget = Array.isArray(targetObj) ? [] : {}
        for (const key in targetObj) {
            //递归拷贝
            cloneTarget[key] = clone(targetObj[key])
        }
        return cloneTarget
    } else {
        //不是对象直接返回
        return targetObj
    }
}
上一篇:CSS3边界图片


下一篇:纯函数