全局对象与原型链

全局对象

在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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。