美文网首页
Javascript 基础

Javascript 基础

作者: 一个做笔记的地方 | 来源:发表于2019-03-18 23:00 被阅读0次
    1、函数防抖和函数节流

    【《javascript高级程序设计》里,函数节流是这里讲的函数防抖。】
    函数防抖: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
    典型的例子:用户输入验证用户名是否有效。假如用户不再输入了,2秒后就验证用户名是否有效,而如果还没到2秒,就重新开始计算2秒。

    /**
     * 函数防抖方法
     * @param Function fn 延时调用函数
     * @param Number interval 间隔多长时间
     */
    let debounce = function(fn, delay){
        let timer = null
        return function() {
            clearTimeout(timer)
            timer = setTimeout(() => {
                fn()
            }, delay)
        }
    }
    

    函数节流:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

    function throttle(fn, interval) {
        let prev = 0
        return function(){
            let now = + new Date()
            if(now - prev > interval){
                fn()
                prev = now
            }
        }
    }
    
    2、js判断数据类型
    • typeof
      可以判断 undefined、string、number、Boolean、function。
      但不能判断 null, typeof null 返回 Object
      也不能判断 Array,typeof 数组 返回 Object
    • instanceof
      用于测试构造函数的 prototype 属性是否出现在对象的原型链中。
      可以用它判断 Function、Array。
      但不要用它判断 Object,因为 数组/函数 instanceof Object 为 true。
    • constructor
      目前运算最快的判断变量类型的方式。
      可以判断除了 nullundefined 类型。
      当检测 nullundefined 类型的 constructor 属性时会报错。
    • Object.prototype.toString.call()
    Object.prototype.toString.call(null);  //  "[object Null]"
    Object.prototype.toString.call(undefined);  //  "[object Undefined]"
    Object.prototype.toString.call(false);  //  "[object Boolean]"
    Object.prototype.toString.call(123);  //  "[object Number]"
    Object.prototype.toString.call('abc');  //  "[object String]"
    Object.prototype.toString.call([]);  //  "[object Array]"
    Object.prototype.toString.call({});  //  "[object Object]"
    Object.prototype.toString.call(function(){});   //  "[object Function]"
    

    PS:在ES5中的判断数组还可以用Array.isArray()。

    3、null 和 undefined 的区别

    阮一峰的:

    • null 在其他语言中与对象有关,typeof null 返回 Object
      undefined 是后来设计的。
    • 在JS最初设计的时候,
      null 是一个表示"无"的对象,转为数值时为0,例如:Number(null)0
      undefined 是一个表示"无"的原始值,转为数值时为NaN。Number(undefined)NaN
    • 后来,修改为:
      null 表示"没有对象",即该处不应该有值。例如:null 作为对象原型链的终点。
      undefined 表示"缺少值",就是此处应该有一个值,但是还没有定义。

    网上的:

    • undefined 希望表示一个变量最原始的状态,而非人为操作的结果 。
      typeof 之所以会判定 null 为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。
    • null 希望表示 一个对象被人为的重置为空对象。
      当一个对象被赋值了null 以后,原来的对象在内存中就处于游离状态,GC 会择机回收该对象并释放内存。因此,如果需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。

    补充:

    • 《重学前端》:undefined 可以作为一个变量来用,程序不会报错,但是如果想把 null 作为变量用程序会报错。所以,通常我们写一些代码防止undefined被污染。
    • JS类型值是存在 32 BIT 单元里,32位有1-3位表示 TYPE TAG,其它位表示真实值,而表示 Object 的标记位正好是低三位都是0。
    • 曾经有提案 typeof null === 'null',但提案被拒绝。

    4、拷贝对象(浅拷贝,深拷贝)
    • Object.assign()
    • es6中的对象展开运算符
    • for in
      缺点:
      1. 属性描述符不能被复制。
        Object.defineProperty() 中定义的描述符不会被拷贝,新对象通过 Object.getOwnPropertyDescriptor() 获取到的值为 undefined
      2. 只能拷贝可枚举属性。
        通过 Object.defineProperty() 定义了 enumerablefalse的属性不会被拷贝。
      3. 若属性的某个值为对象,原对象和拷贝后对象的该属性共享这个对象。
    • JSON.parse(JSON.stringify())
      缺点:
    • 不能拷贝值为函数、正则等的属性。
    • 若对象有循环引用,会报错。

    数组的深拷贝:
    (这里待补充……)

    5、闭包

    参考资料:方应杭的知乎文章

    什么是闭包?
    MDN:闭包是函数和声明该函数的词法环境的组合。
    winter:闭包是绑定了执行环境的函数。它与普通函数的区别是,它携带了执行环境。闭包包含2个部分:
    1、环境部分
    环境:函数的词法环境(执行上下文的一部分)。
    标识符列表:函数中用到的未声明的变量。
    2、表达式部分

    闭包有什么好处?
    闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。

    项目中用过闭包?
    有时候写组件的时候,组件里面的内容不想要被外部访问到,就需要创建一个封闭的环境,不能让别人「直接访问」这个变量。那怎么办呢?用局部变量。但是用局部变量别人又访问不到,怎么办呢?暴露一个访问器(函数),让别人可以「间接访问」。
    比如写签约支付组件:

    // 组件
    (function(){
      var XYS = window.XYS = window.XYS || {}
      XYS.Payment = function(options){
        // 这里是支付组件的内容
      }
    })()
    
    // 组件调用
    XYS.Payment({})
    

    闭包什么情况下会造成内存泄漏?
    闭包不会造成内存泄漏,说内存泄漏是因为 IE 有 bug,IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。

    6、原型链和继承

    参考资料1:JS高级程序设计

    理解函数:

    函数是对象,函数名是指针。函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同。

    ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

    构造函数、原型和实例的关系:

    构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。

    可以这么理解:
    每个函数被创建出来后,它都会有一个prototype属性(即原型),这个属性是一个对象,这个对象包含一个指向该构造函数的指针。而实例都有一个指向这个属性对象的内部指针。

    敲重点:构造函数是一个对象,函数的prototype属性也是一个对象,prototype属性有一个指针指向构造函数。

    假如我们让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链。

    下图中,有5个对象,分别是:superType、superType.prototype、subType、subType.prototype、instance。(你不用去理会这5个对象是实例还是构造函数,也不管是构造函数对象还是字面量对象,总之它们5个都是对象)。

    实例有__proto__属性。
    构造函数有prototype属性。

    分析:
    实例 instance__proto__ 属性是一个指针,指向 SubType.prototype 这个对象。
    构造函数SubTypeprototype属性是一个指针,指向 SubType.prototype 这个对象。
    SubType.prototype这个对象是 SuperType 的一个实例,所以它也有__proto__属性,指向 SuperType.prototype这个对象。
    构造函数SuperTypeprototype属性是一个指针,指向 SuperType.prototype 这个对象。

    实例以及构造函数和原型之间的关系

    如何确定原型和实例的关系?

    • instanceof 操作符:instance instanceof parentType
    • isPrototypeOf(): parentType.isPrototypeOf(instance)

    原型链的问题:
    1、包含引用类型值的原型属性会被所有实例共享。

    function Parent(){
      this.number = [1,2,3]
    }
    function Children(){}
    Children.prototype = new Parent()
    
    var c1 = new Children()
    var c2 = new Children()
    console.log(c1.number === c2.number)  // true
    

    2、在创建子类型的实例时,不能向父类型的构造函数中传递参数。

    参考资料2:简书的某篇文章

    原型是一个对象。每个函数都有一个属性叫做原型,这个属性指向一个对象。
    原型是函数对象的属性,并不是所有对象的属性,对象经过构造函数new出来,那么这个new出来的对象,它的构造函数有一个属性叫原型。

    每次你定义一个函数的时候,这个函数的原型属性也就被定义出来了,也就可以使用了,如果不对它进行显示赋值的话,那么它的初始值就是一个空的Object对象。

    记住:原型只是函数的一个属性!

    function Fn(){}
    console.log(Fn.prototype)
    
    打印结果

    所有函数的构造函数都是Function。

    function Fn(){}
    console.log(Fn.constructor)  // ƒ Function() { [native code] }
    
    继承

    借用构造函数(经典继承 / 伪造对象)
    优点:
    1、子类型构造函数中向超类型构造函数传递参数。
    2、包含引用类型值的原型属性是独立的,不会被所有实例共享。

    function SuperType(name){
      this.name = name;
      this.colors = ["red", "blue", "green"];
    }
    function SubType(){
      //继承了 SuperType
      SuperType.call(this, 'Yumi');
    }
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);    //"red,blue,green,black"
    var instance2 = new SubType();
    alert(instance2.colors);    //"red,blue,green"
    

    ES6里的extends是哪种继承方式?

    相关文章

      网友评论

          本文标题:Javascript 基础

          本文链接:https://www.haomeiwen.com/subject/jixlyqtx.html