全局對象window
在說全局對象之前, 我們先講一個小知識點
JS規(guī)定, 如果首行是大括號, 一律解釋為代碼塊
{foo: 123}
如果要解釋為表達式(對象), 則必須在大括號前加圓括號
({foo: 123})
global(瀏覽器里就是window)
window的屬性大致分為兩類, 一類是ECMAScript規(guī)程, 一類是瀏覽器環(huán)境私有的, 如
ECMAScript規(guī)定 | 私有(chrome/firefox等) |
---|---|
parseInt | alert |
parseFloat | prompt |
Nmuber | comfirm |
String | console |
Boolean | document |
setTimeout | history |
window有哪些屬性和方法可以查看mdn
這里給大家說一點需要注意的地方, 我們用字面量的方式聲明數(shù)字和用new Number()聲明的數(shù)字是不一樣的, 用new Number()聲明的數(shù)字是一個對象
let n1 = 1
let n2 = new Number(1)
n1是一個簡單類型, n2實際上是一個對象
當我們用n1.toString()時, js會先把n1臨時轉換成Number對象, 然后使用對象的toString()方法, 轉換之后臨時對象Number即銷毀
下面的代碼就很明白說明這個過程了
let n = 1
n.name = 'Adam'
n.name //undefined
上面的代碼, 我們給n一個name屬性, 這是js解釋器會把n轉換成一個Number對象, 然后給這個對象賦值'Adam', 然后這個對象就銷毀了
當我們調用n.name時,js解釋器會再創(chuàng)造一個新的Number對象, 這個對象并沒有name屬性, 所以是undefined
這個對也String()等也同樣適用
幾個有用的string類型API
trim()
去掉字符串前后的空格
' username '.trim()
'uername'
concat()
連接兩個字符串,返回一個新字符串
let s1 = 'Hello'
let s2 = 'World'
s1.concat(s2)
'HelloWorld'
slice(index, length)
截取字符串, 返回一個新字符串
let s1 = 'Hello'
s1.slice(0,2)
'He'
replace(oldLetter, newLetter)
替換第一個匹配字符串, 并替換成新字符, 返回一個新字符串
let s1 = 'Hello'
s1.replace('l', 'o')
'Heolo'
構造函數(shù)
JS使用構造函數(shù)作為對象的模版, 構造函數(shù)在不用new時, 就是普通函數(shù)
let b = new Vehicle()
和 let b = new Vehicle
等價
不使用new時, 構造函數(shù)變成就變成普通函數(shù), this這時代表全局對象
let Vehicle = function () {
this.price = 1000
}
let v = Vehicle()
v //undefined
price //1000
new 做了什么
- 創(chuàng)建一個空對象, 作為將要返回的對象實例
- 將這個空對象的proto, 指向構造函數(shù)的prototype屬性
- 將空對象賦值給函數(shù)內部this關鍵字
- 開始執(zhí)行構造函數(shù)內部的代碼
- 返回構造函數(shù)
- 如果構造函數(shù)內部有return語句, 而且return一個對象, new命令會直接返回該對象
this 關鍵字
全局環(huán)境
function fn() {
console.log(this === window)
}
fn() //true
構造函數(shù)
這個就是上面提到的new
對象的方法
let obj = {
foo: function() {
console.log(this)
}
}
obj.foo() //obj
下面情況, 會改變this指向
(obj.foo = obj.foo)() //window
(false || obj.foo)() //window
(1, obj.foo)() //window
第一個等價于
(obj.foo = function(){console.log(this)})()
(function() {console.log(this)})()
第二個等價于
(false || function() {console.log(this)})()
第三個等價于
(1, funciton() {console.log(this)})()
如果this所在的方法不在第一層, this就只是指向當前一層對象, 不會繼承更上層
let a = {
p: 'Hello',
b: {
m: function() {
console.log(this.p)
}
}
}
a.b.m() //undefined
所以請使用call apply bind方法強制綁定this, 就不會亂了
關于this的用法還有另一篇文章中也提到了, 有興趣的可以看一看
原型
我們聲明一個函數(shù)
function fn(){
}
對于每一個聲明的函數(shù), 都會有一個prototype
屬性, 有且只有函數(shù)才有prototype
屬性
所以fn
也會有一個fn.prototype
的屬性
當我們對函數(shù)使用new
操作符時, 就會生成一個實例對象, fn
就是這個實例對象的構造函數(shù)
實例對象就會自動生成一個__proto__
的屬性,指向他的構造函數(shù)的prototype
屬性, 所以當我們new fn()
時, new
出來的實例會自動有一個__proto__
屬性指向fn.prototype
這個prototype
屬性, 我們就給它取個名字, 叫原型
我們給fn函數(shù)new一個實例對象
function fn() {}
let obj = new fn()
obj.__proto__ === fn.prototype // true
上面的話總結起來, 就是下面這個圖
我們繼續(xù)延續(xù)上面的說下去,其實prototype
屬性, 仍然有__proto__
屬性滨嘱, 所有函數(shù)的prototype
屬性最終都會指向Object.prototype
fn.prototype.__proto__ = Object,prototype
因為Object.prototype
中包含toString
等方法, 這就是為什么所有對象都能調用toString
等方法的原因
我們可以發(fā)散一下
let string = new String('aaa')
string.__proto__ === String.prototype //true
string.__proto__.__proto__ === Object.prototype //true
我們繼續(xù)發(fā)散一下, 我們創(chuàng)建一個函數(shù), 會不會有__proto__
呢
function fn() {}
fn.__proto__ === Function.prototype //true
對于聲明函數(shù)來說,fn
有一個prototype
, 并且fn.prototype.__proto__ === Object.prototype
fn還有一個__proto__
, 并且fn.__proto__ === Function.prototype
是不是有點燒腦呢瓤介?
再來個燒腦的, Object, Number, String, Boolean, Function這些構造函數(shù)的proto指向
Object.__proto__ === Function.prototype //true
Number.__proto__ === Function.prototype //true
String.__proto__ === Function.prototype //true
Boolean.__proto__ === Function.prototype //true
Function.__proto__ === Function.prototype //true
我們發(fā)現(xiàn),所有構造函數(shù)的proto都指向Function.prototype, 因為所有函數(shù)都是通過new Function()
創(chuàng)造出來的
總結下來, 就是下面的圖
以及