美文网首页
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