美文网首页饥人谷技术博客
全局对象与原型链

全局对象与原型链

作者: 饥人谷_AaronXu | 来源:发表于2019-08-01 19:02 被阅读55次

    全局对象

    在ECMA Script 标准中,全局对象叫做 global, 全局对象不一定是 window,但在浏览器(早于ES)环境中,默认的全局对象就是 window

    global.parseInt
    global.parseFloat
    

    windows

    实际是一个哈希表,有许多属性,我们称为全局变量。

    全局变量

    常用全局变量举例,详细使用可查看MDN,实际应用中可以省略 window,比如 window.alert() 可以直接写成 alert()

    ECAM Script 规定 私有 (chrome/Firefox)
    global.parseInt() window.alert (弹框提示)
    global.parseFloat() window.prompt(用户填写)
    global.Number() window.confirm(确认)
    global.String() window.console.log
    global.Boolean() window.console.dir
    global.Object() window.document(文档,DOM,W3C规定)
    global.Symbol() window.document.createElement
    global.setTimeout(function(){},time) window.document.getElementById
    window.history(浏览器,BOM)

    ECMA规定里的几个重要函数/对象

    Number()

    1. 基本类型的Number

      var n1 = Number('1') 
      
    2. new创建的实例对象

      var n2 = new Number(1)
      
    内存图

    变量 n1

    • stack栈内存:1
    • heap堆内存:/

    变量 n2

    • stack栈内存:Address 44

    • heap堆内存:

      Address  44: 
      // A hash
      // Number {1}
      // --------------------------
      // __proto__: Number
      // [[PrimitiveValue]]: 1
      // --------------------------
      // 相关属性举例:
      n2.toString() // "1"
      n2.ValueOf()  // 1
      n2.toExponential()  // 1e+0
      n2.toFixed(2) // 1.000
      

    然而在Java Script中,我们没必要使用第二种方式申明对象,尽管 n1 只是一个简单类型的数值,不是一个对象,但 n2 所含的这些属性,在Java Script中,我们也可以和 n2 一样简单通过点运算符(.)得到。

    n1.toString()
    n1.ValueOf()
    n1.toExponential()
    n1.toFixed(2)
    // 实际上述属性不存在,但是JS创建了一个临时对象
    // temp = new Number(n1)
    // n1.toString() 实际上得的的结果是 temp.toString()的结果
    // temp 是临时存在的对象,用完就会被抹除
    

    所以,在Java Script中,简单类型的数据也可以添加不存在的属性,然而实际是在给临时创建的对象添加属性,用完就被抹除,所以当我们再试图读取之前添加的属性时,原来的临时对象已经被抹除,然而在新的临时对象中,该属性不存在(可内存图辅助加深理解)

    n1.balabala = ''
    n1.balabala // undefined
    

    String()

    1. 基本类型的String

      var s1 = String('qw ') 
      
    2. new创建的实例对象

      var s2 = new String(s1)
      console.log(s2)
      // A hash
      // String {"qw "}
      // --------------------------
      // __proto__: String
      // [[PrimitiveValue]]: "qw "
      // 0: "q"
      // 1: "w"
      // 2: " "
      // length: 3
      // --------------------------
      // 相关属性举例:
      s2.charAt(1)  // "w",获取某一个索引对应的字符,等价于 s2[1]
      s2.charAt(3)  // ""
      s2.charCodeAt(1) // 119,获取某一个索引对应的字符的编码码点,十进制Unicode
      s2.charCodeAt(3) // NaN
      s2.charCodeAt(1).toString(16) // 77,十六进制的Unicode
      s2.trim() // "qw",去掉字符串内空格
      s2.concat("asd") // "qw asd",连接字符串
      s2.slice(0,2) // "qw",从s2[0]开始算的前两个字符串
      s2.replace(" ","e") // "qwe"," "替换为"e"的一个字符串
      s2.substring(0,2) // "qw",以s2[0]我首字母,s2[2](不包括)结束的字符串
      s2.includes(" ")  // true,返回一个布尔值判断是否" "在s2中
      

    更多 String 属性 可以google搜索 String MDN 学习。

    Boolean()

    1. 基本类型的Boolean

      var b1 = true
      
    2. new创建的实例对象

      var b2 = new Boolean(true)
      console.log(b2)
      // A hash
      // Boolean {true}
      // --------------------------
      // __proto__: Boolean
      // [[PrimitiveValue]]: true
      // --------------------------
      // 相关属性举例:
      b2.valueOf()  // true
      b2.toString()  // "true"
      // 注意:b2是对象,不是falsy值
      

    Object()

    var o1 = {}
    var o2 = new object({})
    o1 === o2  
    // false
    // o1 和 o2 没有任何区别,但并不相等
    // 通过内存图分析,事先申明的对象,在stack栈内存中,两者在堆内存的地址不同
    // 自然在heap堆内存中,不同地址引用的空对象也不是同一个
    
    变量 Stack栈内存 Heap堆内存
    o1 堆内存地址1 -------> {}
    o2 堆内存地址2 -------> {}

    往往事先声明的对象一般都是不相等的,除非两者把其中一个变量堆内存的地址()赋给另外一个变量。

    var o1 = {}
    var o2 = o1
    o1 === o2 
    // true
    // o1 和 o2 都指向了堆内存中同一个空对象
    // o1的堆内存地址赋值给了o2
    // 赋值操作后,两者保存了同一个堆内存对象地址
    // 注意:所以改变任何一个变量都会互相影响,除非对象地址发生变化
    o2.name = 'aaa'
    console.log(o2) // {name: 1}
    console.log(o1) // {name: 1}
    

    JavaScript 原型链

    我们从上述介绍的几个函数对象(Number()String()等)中发现 ,这实例对象都有一些共有属性,比如 toString()valueOf() 等等。如果把这些属性都当作对象的私有属性,给每个对象单独存这些共有的属性,显然是十分浪费内存的。JS的做法是,每一个实例对象都有一个私有的属性(__proto__)来表示这些共有的属性,但并不直接存这些共有的属性,而是指向它的构造函数的原型对像(prototype)。

    举例:

    var n1 = new Number()
    var n2 = new Number()
    n1 === n2 // false
    n1.toString === n2.toString // true
    n1.__proto__ === n2.__proto__ // true 
    n1.__proto__ === Number.prototype // true
    // n1,n2都指向一个原型对象 Number.prototype
    
    

    同时,我们也注意到了除了Object对象之外,比如Number对象,它的原型对象(Number.prototype)也有一个自己的原型对象。

    Number.prototype.__proto__ === Object.prototype // true
    Object.prototype.__proto__ // null
    

    Number对象的原型对象是一个Object对象,Object对象的原型对象是null。

    延伸开来,每一个实例对象都有一个__proto__指向它的构造函数的原型对象(prototype),然后其原型对象也有一个自己的原型对象,这样层层往上直到Object的原型对象为null,null没有原型对象,并且我们称其为原型链的最后环节。

    通过上文对 几个函数/对象的了解 (NumberStringBoolean)我们可以发现,这些对象都有他们独特的属性。也有一些他们共同拥有的属性(Object的共有属性)。上面,我们已经分析了Number对象的原型链,可以看到Number对象的原型对象是Number.prototype,里面存了Number对象独有的共有属性。

    n1.__proto__ === Number.prototype // true
    

    Number对象的原型对象的原型对象是Object.prototype,里面存了Object对象普遍有的共有属性。

    n1.__proto__.__proto__ === Object.prototype // true
    

    当JS需要调用某个对象的属性时,它不单单在该对象上搜寻,同时也会搜寻它的原型以及原型的原型,层层往上直到找到或者搜到原型链的顶点,所以我们才能直接调用Number对象的toFixedNumber.prototype中)和toString属性(Object.prototype中)

    参考自:继承与原型链 - JavaScript | MDN

    区分 __proto__prototype

    简单来说__proto__总是跟在一个对象后面,它是用来表示指向这个对象的原型,而prototype,我们可以发现它总是跟在一个构造函数的后面,prototype是自带在浏览器中的,即便不写任何代码,prototype也是存在的,且无法修改。

    构造函数(constructor)

    构造函数主要有以下几个特点

    1. 首字母大写
    2. new来调用生成实例对象
    3. 函数内部使用this对象来指向要生成的实例对象

    下表概括了JS中数据类型的构造函数

    数据类型 构造函数
    数值 Number
    字符串 String
    布尔值 Boolean
    null
    undefined
    符号
    函数(对象) Function
    数组(对象) Array
    普通对象 Object

    结合这些,我们就能更清晰地解释原型链。

    推论

    举例:

    var s = new String()
    s.__proto__ === String.prototype // true
    

    上述例子中,被构造函数String创造的实例对象s__proto__指向Stringprototype属性。

    String.prototype 就表示String的原型对象。

    延伸开来,被构造函数Func创造的实例对象func__proto__指向Funcprototype属性。

    Func.prototype 就表示Func的原型对象,也就是

    var func = new Func()
    func.__proto__ === Func.prototype // true
    
    延伸1

    同时我们注意到构造函数Func的原型对象 Func.prototype本身也是一个Object(对于Number,String等),所以当我们把它当做构造函数Object的一个示例对象,我们可以得到:

    举例:

    Number.prototype.__proto__ === Object.prototype // true
    Function.prototype.__proto__ === Object.prototype // true
    

    这个结果也就解释了上文中为何Number对像的原型对象的__proto__指向了Objectprototype属性。

    延伸2

    函数对象的构造函数是Function,而构造函数比如Number都是Function的实例对象。所以我们可以得到以下结果:

    Number.__proto__ === Function.prototype // true
    String.__proto__ === Function.prototype // true
    Object.__proto__ === Function.prototype // true
    Boolean.__proto__ === Function.prototype // true
    Function.__proto__ === Function.prototype // true
    // 在这,我们可以把Function函数本身也看成被其本身创造的一个实例对象
    
    注意

    我们要注意的是,Object的原型对象的__proto__指向的是null而不是Object,prototype

    Object.prototype.__proto__ === null // true
    

    相关文章

      网友评论

        本文标题:全局对象与原型链

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