基础知识补充

136 阅读6分钟

判断数据类型

  • 基本数据类型:number,string,boolean,null,undefined
  • 复杂数据类型:Object,Function(和object区别为可执行),Array
  • typeOf(typeOf 3 ==='number'):返回为字符串,判断除了null之外的数据基本类型,typeOf null返回为object。但是判断方法 可以输出 'function'
  • instanceof({} instanceof Object): 根据原型链寻找来判断对象的具体类型,不能判断基本数据类型。例如A instanceof B,B构造函数的显示原型对象在A对象的原型链上(顺着__proto__找),则返回true,即类似于A.__proto.__proto__=B.prototype
  • Object.prototype.toString.call():判断一切类型,返回为类型于[object,String]等形式字符串。 拓展:
  • undefined和null区别?
    • undefined代表定义了为赋值
    • null为定义并赋值了,只是值为null
  • 什么时候给变量赋值为null?
    • 将一个变量将要赋值为对象的时候,初始赋值可以设置为null
    • 如果本来是一个对象类型的变量,然后再赋值为null,这样可以让垃圾回收标记为垃圾对象,到垃圾回收的时候就会把该对象回收。
  • 严格区别变量类型与数据类型?
    • 数据类型(基本类型+对象类型)

    • 变量类型(基本类型+引用类型):即栈内存储的变量的类型,即保存的为基本数据类型的数据,还是堆的地址值

new做的事情

  • 创建一个空对象
  • 把该对象的__proto__属性指向构造函数的prototype属性
  • 执行构造函数的代码,即挂载构造函数的对应属性和方法
  • 返回该对象

原型链

  • 一个实例读取属性值时,会首先在当前对象的区域中查找,如果没有找到就会顺着原型链往上一级查找,直到查找到尽头都没查找到则报错
  • 显示原型和隐式原型
    • 每个函数都会存在有一个prototype属性,该属性默认为一个空实例对象,使用函数.prototype获取的就是显示原型
    • 每个实例都会有一个__proto__属性,该属性和构造函数的prototype指向的为同一个对象,而使用实例.__proto__获取则为隐式原型
  • 原型链连接的原型都是一个实例对象,实例对象都会存在有__proto__属性,而最终的__proto__为null,即Object的__proto__为null
  • 原型链本质上是隐式原型链,按照隐式原型链查找,即按照__proto__查找

变量提升和函数提升

  • 通过var定义的变量,在定义语句之前就可以访问到值,值为undefined
  • 通过function声明的函数,在之前就可以直接调用,值为函数对象
console.log(a)//undefined
fn()//执行fn内容

var a = 1;//变量提升
function fn2(){}//会出现函数提升
var b = function(){}//不会出现函数提升
  • 先执行变量提升再执行函数提升,因此出现函数和变量名重复的时候,变量提升后会是函数内容。

执行上下文

  • 全局执行上下文——window
    • 在执行全局代码前将window确定为全局执行上下文
    • 对全局数据进行预处理
      • var定义的全局变量=》undefined,添加为window的属性
      • function声明的全局函数=>赋值(fun),添加为window的方法
      • this=>赋值(window)
    • 开始执行全局代码
  • 函数执行上下文
    • 在调用函数时,准备调用函数体之前,创建对应的函数执行上下文对象
    • 对局部数据进行预处理
      • 形参变量=>赋值(实参)=>添加为执行上下文属性
      • arguments=>赋值(实参列表),添加为执行上下文的属性
      • var定义的局部变量=>undefined,添加为执行上下文的属性
      • function声明的函数=>赋值(fun),添加为执行上下文的方法
      • this=>赋值(调用函数的对象)
    • 开始执行函数体代码

作用域和作用域链

  • 全局作用域、函数作用域、块作用域
  • 相对于执行上下文来说,作用域是静态的,例如执行了两次函数,就会产生两个函数执行上下文,但作用域只在声明的时候就已经被确认,不会动态生成。数量为定义函数的数量+一个全局作用域
    • 全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是函数调用时
    • 全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建
    • 作用域是静态的,只要函数定义好了就一直存在,且不会再变化
    • 上下文环境是动态的,调用函数时创建函数调用结束时上下文环境就会被自动释放
    • 执行上下文环境是属于所在的作用域,全局上下文环境=>全局作用域,函数上下文环境=>对应的函数作用域

image.png

  • 作用域作用:隔离变量,不同作用域下同名变量不会冲突
  • this确认为执行上下文创建时确认的,是动态确定的。而变量寻找是按照作用域链查找的,只会在定义的时候就确定了。

image.png

闭包

  • 当一个嵌套的内部函数引用了嵌套的外部函数的变量时,就会产生闭包。在内部函数被声明创建的时候就才会产生闭包
  • 闭包是什么:嵌套的内部函数,内部函数引入了父级函数的变量
  • 闭包作用
    • 使用函数内部的变量在函数执行完后,依然存活在内存中(延长了局部变量的生命周期)
    • 让函数外部可以操作函数内部的数据
  • 函数执行完后,函数内部声明的局部变量除了被闭包使用的变量之外的所有变量都会正常销毁
  • 闭包缺点:内存泄漏。

image.png

image.png

创建对象

  • 属性方法未确定时候使用: var obj = new Object()
  • 字面量创建(如果多个相似对象会存在多重复代码): var obj = {name: 'aaa'}
  • 工厂模式创建:(动态创建对象并返回,适用于创建多个相似对象)
function createPerson(name,age){
    var obj = {
        name: name,
        age: age,
        setName: function{..}
    }
    return obj
}
  • 原型链继承 子类型的原型为父类型的一个实例对象,即Children.prototype = new Parent()实现方法复用
  • 寄生继承 子类构造函数调用了父类构造函数,从而实现属性的初始化操作,即在子类构造函数。
funtion Children(name,age,sex){
    Parent.call(this,name,age)
    this.sex=sex;
}
  • 组合继承
funtion Parent(name,age){
    this.name = name;
    this.age = age;
}
Parent.prototype.setName = function(name){
    this.name = name
}

funtion Children(name,age,sex){
    Parent.call(this,name,age)//为了得到属性
    this.sex=sex;
}

Children.prototype = new Person()//为了得到父类方法
Children.prototype.constructor = Children//修正构造器