全局对象
在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()
-
基本类型的Number
var n1 = Number('1')
-
用
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()
-
基本类型的String
var s1 = String('qw ')
-
用
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()
-
基本类型的Boolean
var b1 = true
-
用
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没有原型对象,并且我们称其为原型链的最后环节。
通过上文对 几个函数/对象的了解 (Number
,String
,Boolean
)我们可以发现,这些对象都有他们独特的属性。也有一些他们共同拥有的属性(Object
的共有属性)。上面,我们已经分析了Number对象的原型链,可以看到Number对象的原型对象是Number.prototype
,里面存了Number对象独有的共有属性。
n1.__proto__ === Number.prototype // true
Number对象的原型对象的原型对象是Object.prototype
,里面存了Object对象普遍有的共有属性。
n1.__proto__.__proto__ === Object.prototype // true
当JS需要调用某个对象的属性时,它不单单在该对象上搜寻,同时也会搜寻它的原型以及原型的原型,层层往上直到找到或者搜到原型链的顶点,所以我们才能直接调用Number对象的toFixed
(Number.prototype
中)和toString
属性(Object.prototype
中)
区分 __proto__
和prototype
简单来说__proto__
总是跟在一个对象后面,它是用来表示指向这个对象的原型,而prototype
,我们可以发现它总是跟在一个构造函数的后面,prototype
是自带在浏览器中的,即便不写任何代码,prototype
也是存在的,且无法修改。
构造函数(constructor)
构造函数主要有以下几个特点
- 首字母大写
- 用
new
来调用生成实例对象 - 函数内部使用
this
对象来指向要生成的实例对象
下表概括了JS中数据类型的构造函数
数据类型 | 构造函数 |
---|---|
数值 | Number |
字符串 | String |
布尔值 | Boolean |
null | 无 |
undefined | 无 |
符号 | 无 |
函数(对象) | Function |
数组(对象) | Array |
普通对象 | Object |
结合这些,我们就能更清晰地解释原型链。
推论
举例:
var s = new String()
s.__proto__ === String.prototype // true
上述例子中,被构造函数String
创造的实例对象s
的__proto__
指向String
的prototype
属性。
String.prototype
就表示String
的原型对象。
延伸开来,被构造函数Func
创造的实例对象func
的__proto__
指向Func
的prototype
属性。
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__
指向了Object
的prototype
属性。
延伸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
网友评论