函数调用位置
就是函数在代码中被调用的位置(而不是声明的位置),寻找调用位置最重要的就是分析调用栈
。算法
这个时候的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)
}
复制代码