函數調用位置
就是函數在代碼中被調用的位置(而不是聲明的位置),尋找調用位置最重要的就是分析調用棧
。算法
這個時候的this是window
(注意:若是使用嚴格模式,則不能將全局對象用於默認綁定
)數組
雖然this的綁定規則徹底取決於調用位置,可是隻有函數運行在非strict mode下時,默認綁定才能綁定到全局對象;在嚴格模式下調用函數則不影響默認綁定。安全
function foo(){
'use strict'
console.log(this.a)
}
var a = 2;
foo(); // TypeError : this is undefined
function foo(){
console.log(this.a)
}
var a = 2;
(function(){
'use strict'
foo() // 2
})()
複製代碼
調用位置是否有上下文對象,或者說是否被某個對象擁有或者包含,這時候隱式綁定規則會把函數調用中的this綁定到這個上下文對象。bash
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
obj.foo() // 2
複製代碼
隱式規則會有時候丟失綁定對象。app
一、定義函數別名致使丟失函數
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo;
var a = 'xx'
bar() // 'xx' // 這個時候致使this 指向的全局
複製代碼
二、當作回調函數ui
function foo(){
console.log(this.a)
}
var obj = {
a:2,
foo:foo
}
var a = 'xx'
setTimeout(obj.foo,100) // 'xx'
複製代碼
bind實現考慮做爲構造函數
)傳遞的第一個參數是this對象,若是傳入的是一個原始值來當作this的綁定對象,這個原始值會被轉換成它的對象形式(也就是new String()、new Boolean()或者new Number()),就是裝箱。this
function foo(){
console.log(this.a)
}
var obj = {
a:2
}
foo.call(obj) // 2
function foo(something){
console.log(this.a,something);
return this.a + something;
}
// 輔助函數bind
function bind(fn,obj){
return function(){
return fn.apply(obj,arguments)
}
}
var obj = {
a:2
}
var bar = bind(foo,obj)
var b = bar(3) ;
console.log(b) // 5
複製代碼
使用new來調用函數,或者說發生構造函數調用時,會自動執行下面的操做。spa
一、建立一個全新的對象。prototype
二、這個新對象會被執行[[prototype]]鏈接
三、這個新對象會綁定到函數調用的this
四、若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a) // 2
複製代碼
new綁定 > 顯示綁定 > 隱式綁定 > 默認綁定
若是顯示綁定的this傳遞的null
或者undefined
,實際應用的是默認綁定規則。
建立一個安全的空對象的最簡單的方法就是使用Object.create(null),它建立的空對象不會建立Object.prototype這個委託,因此他比{}更空。
賦值表達式p.foo = o.foo 的返回值是目標函數的引用
,所以調用位置是foo()而不是p.foo() 或者 o.foo() ,所以這裏會應用默認綁定。
function foo(){
console.log(this.a)
}
var a = 2;
var o = {a:3,foo:foo}
var p = {a:4}
o.foo() //3
(p.foo = o.foo)() // 2
複製代碼
對於默認綁定來講,決定this綁定對象並非調用位置是否處於嚴格模式
,而是函數體是否處於嚴格模式
。若是函數體處於嚴格模式,this會被綁定到undefined,不然this會被綁定到全局。
箭頭函數不使用this的四種標準規則,而是根據外層(函數或者全局)做用域來決定this。
function foo(){
return (a) => {
// this 繼承自foo()
console.log(this)
}
}
var obj1 = {
a:2
}
var obj2 = {
a:3
}
var bar = foo.call(obj1);
bar.call(obj2) // 2
複製代碼
主要類型
內置對象
一、在對象中,屬性名永遠都是字符串
,若是你使用string(字面量)之外的其餘值做爲屬性名,那它首先會被轉換爲一個字符串,即便是數字也不例外。
二、數組也是對象,因此雖然每一個下標都是整數
,你仍然能夠給數組添加屬性.可是數組的length值並未發生改變
三、若是你試圖向數組添加一個屬性,可是屬性名是一個數字
,那麼就會變成數值下標(所以會修改數組的內容而不是添加一個屬性)
var myArray = ['foo',42,"bar"];
myArray.baz = "baz";
myArray.length // 3
myArray.baz // "baz"
複製代碼
對象默認的屬性描述符
var myObject = {
a:2
}
Object.getOwnPropertyDescriptor(myObject,a)
/*
{
value:2,
writable:true, // 可寫
configurable:true, // 可配置
enumerable:true // 可枚舉
}
*/
複製代碼
本身設置屬性描述符
var myObject = {
}
Object.defineProperty(myObject,a,{
value:2,
writable:true,
configurable:true,
enumerable:true
})
複製代碼
writable
決定是否能夠修改屬性的值,true爲能夠,false爲不能夠
configurable
決定屬性是否可配置,若是經過defineProperty定義一個屬性configurable爲false,則不能夠在進行配置(defineProperty),configurable:false還會禁止刪除這個屬性
注意:即便屬性是configurable:false ,咱們仍是能夠把writable的狀態由true改成false,可是沒法由false改成true
enumerable
控制屬性是否會出如今對象的屬性枚舉中,好比說for..in循環。
一、對象常量
結合writable:false和configurable:false就能夠建立一個真正的常量屬性(不可修改、重定義或者刪除)
二、禁止擴展
若是想禁止一個對象添加新屬性而且保留已有屬性,可使用Object.preventExtensions()
Object.preventExtensions(myObject)
複製代碼
三、密封 Object.seal()(Object.preventExtensions加全部屬性標記爲configurable:false)
四、凍結 Object.freeze() (Object.seal加把全部的"數據訪問"屬性標記爲writable:false)
[[Put]]被觸發時,實際的行爲取決於許多因素,包括對象中是否已經存在這個屬性(這是最重要的因素),若是已經存在這個屬性,[[Put]]算法大體會檢查下面這些內容。
一、屬性是不是訪問描述符
?若是是而且存在setter就調用setter。
二、屬性的數據描述符
中writable是否爲false?若是是,在嚴格模式下靜默失敗,在嚴格模式下拋出TypeError異常。
三、若是都不是,將該值設置爲屬性的值。
在ES5中可使用getter和setter部分改寫默認操做,可是隻能應用在單個屬性上,沒法應用在整個對象上。getter是一個隱藏函數,會在獲取屬性時
調用。setter也是一個隱藏函數,會在設置屬性時
調用
當你給一個屬性定義getter、setter或者二者都有時,這個屬性會被定義爲"訪問描述符"(和"數據描述符"相對)。對於訪問描述符來講,Javascript會忽略它們的value和writable特性,取而代之的是關心set和get特性。
var myObject = {
// 給a定義一個getter
get a() {
return 2
}
}
Object.defineProperty(
myObject,
"b",
{
get:function(){
return this.a *2
},
enumerable:true
}
)
myObject.a = 3 // 沒有設置成功 由於沒有set,因此通常get和set要成對出現
myObject.a // 2
myObject.b // 4
複製代碼
in操做符會檢查屬性是否在對象及其[[Prototype]] 原型鏈中。(實際上檢查的是某個屬性名
是否存在)
hasOwnProperty只會檢查屬性是否在myObject對象中,不會檢查[[Prototype]]鏈。
enumerable:true 就至關於"能夠出如今對象屬性的遍歷中"。
propertyIsEnumerable會檢查給定的屬性名是否直接存在於對象中
(而不是在原型鏈上)而且知足enumerable:true。
Object.keys會返回一個數組,包含全部可枚舉屬性。
Object.getOwnPropertyNames會返回一個數組,包含全部屬性,不管他們是否可枚舉。
in和hasOwnProperty的區別在因而否查找[[Prototype]]鏈。
object.keys和object.getOwnPropertyNames都只會查找對象直接包含的屬性。
若是想直接遍歷數組的值可使用for..of循環語法。(若是對象自己定義了迭代器的話也能夠遍歷對象)
for..of循環首先會向被訪問對象請求一個迭代器對象,而後經過調用迭代器對象的next()方法來遍歷全部返回值。
var myArray = [1,2,3]
for(var v of myArray){
console.log(v) // 1,2,3
}
複製代碼
數組有內置的@@iterator,所以for..of能夠直接應用在數組上。咱們使用內置的@@iterator來手動遍歷數組,
var myArray = [1,2,3];
var it = myArray[Symbol.iterator]();
it.next() // {value:1,done:false}
it.next() // {value:2,done:false}
it.next() // {value:3,done:false}
it.next() // {done:true} value爲當前的遍歷值 done是一個布爾值,表示是否還有能夠遍歷的值
複製代碼
普通的對象如何實現for..of遍歷(須要本身定義Symbol.iterator)
var myObject = {
a:2,
b:3
}
Object.defineProperty(myObject,Symbol.iterator,{
enumerable:false,
writable:false,
configurable:true,
value:function(){
var o = this;
var idx = 0;
var ks = Object.keys(o);
return {
next:function(){
return {
value:o[ks[idx++]],
done:(idx > ks.length)
}
}
}
}
})
// 手動遍歷myObject
var it = myObject[Symbol.iterator]();
it.next(); // {value:2,done:false}
it.next();// {value:3,done:false}
it.next();// {value:undefined,done:true}
// 用for..of遍歷myObject
for(var v of myObject){
console.log(v)
}
複製代碼