开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20天,点击查看活动详情
误解
第一误解是this是指向函数本身。 函数本身直接用函数名表示就可以。this不行。
第二个误解是 this是受到作用域的影响。不会受到作用域的影响。
this的四条规则
1,默认绑定window,如果是严格模式下是绑定undefined
2,强制绑定(call,bind,apply).如果是基础类型则会被装箱。
3,隐式绑定(就是谁点它,就是谁)
- 参数的传递 。foo.bar这样的参数传递,参数接收已经改变了变量了。setTimout就是传递回调函数,然后在里边执行这个回调函数。我们把这种情况叫做丢失this指向。
4,new绑定
-
其实所有的函数都是普通函数,当使用new时就都变成了构造函数。其实也可以说是new的构造操作。new总共分成四个步骤,第一是 创建一个全新的对象 , 第二个是新对象和函数之间的原型 第三是 新对象绑定this 第四是 如果函数没有返回其他值,那么就返回this对象。
this四种绑定的优先级
在new里边 >显示绑定 >隐士绑定>默认绑定
绑定例外
被忽略的this
如果是null 和 undefined,传入硬绑定中作为this的绑定值得话,这些值会被忽略。
为了不使全局污染,则使用{}来替代null和undefined。不然会默认绑定的全局的window上,这样会导致全局污染。
间接绑定
间接绑定的注意事项:
o.foo();o是隐式绑定。但是如果是 p.foo=o.foo ,那么p.foo()不是隐式绑定,p.foo就是一个函数的引用。
箭头函数
箭头函数的this就是当前作用域的this。
对象
对象有两种构造形式,一种是文字的构造形式,一种是构造函数的形式。
{} 和 new Object();
六种类型
每一种类型都对应了一个对象。
虽然我们在操作字面量,但是在操作字面量的方法时,其实字面量是转换成了对象的。
六中类型中 null和undefined是没有构造形式的,只有文字字面量的形式。相反Date只有构造的形式。
内置对象:
Number,String,Boolean 有字面量,其实实现的还是构造对象
Date,只有构造函数一种创建方式。
Array,Function,Object,RegExp,不管是字面量创建还是构造函数创建,都是一个对象。
Error. 一般自动创建
. 和 [] 的访问:
.是属性访问,[] 是键访问。区别是 .是需要符合命名规则的,并且必须是字符串,如果不是字符串的会转换成字符串。
但是键访问不需要。例如:"---",变量中间是空格等不规则的字符。 还要一个重要的区别是,键访问可以是变量。但是属性访问是不可以的。
Es6中还增加了键访问的计算特性。可以在[]中进行简单计算。
属性和方法
Object.defineProperty(obj,{
value:"",
writable:是否可写(就是不能够进行修改)
configurable:"",不可配置
enumrable:枚举
});
TypeError 无法修改一个不可写的属性。
上述设定的内容,如果在严格模式下则会报错。
configurable:不可修改,同时也不可删除。并且这操作是单向的,不能够修改回来。不管硬性操作会报错。
不变性
对象的常量:
设置wribale和configurable 设置为false则这个就是常量,不能再改变的。
get 和 set
get和set 可以在字面量对象中设置,也可以在Object.defineProperty中进行设置。
在字面量对象中设置get是{get a(){} }这样的方式,属性是写在get函数名的后边的。
注意:如果只设置get,不设置set,那么会导致对象每次返回的都是get里边的值。而没有改变原来的值。
存在性
属性的存在性主要是两个方式,一个是使用in来进行判断一个是使用hasOwnProperty
in是包括原型链上的
hasOwnProperty是不包括原型链上的。但是是其实它自己是通过property这个原型链访问的。
Object.create()的理解和分析
作用是返回一个对象,给该对象增加一条原型链的属性,指向该对象。
//增加一条原型链 指向 传递的参数
let obj={a:1}
//下边这两句话几乎是对等的
let obj2=Object.create(obj);
obj3.__proto__=obj;
Object.create(null); 如果是这么写的话,那么就是原型链是指向空的。
Object.keys()和Object.getOwnPropertyNames()
Object.keys();包含可枚举的属性。
Object.getOwnProertyNames();包含所有的属性,不管是不是可枚举的。
这个两个的区别是都只会查找对象直接包含的属性。
propertyIsEnumrable()
检查在对象本身上属性
原型
Object.property的属性修改
如果属性在对象中没有,那么会通过原型链条进行查找。如果查找不到会,会在对象中新增一个属性。如果能查找到则会修改,如果修改的话,会存在隐士屏蔽,一般是用于在原型链条上进行获取。
还有一种情况是:在原型链条上可以查找但是是只读属性。那么通过=号的操作就会被忽略,同时在这条原型链条上都不能增加同名属性。这样是保护父属性的一种策略。但是这种策略在使用Object.proerty中是可以打破的。
隐士屏蔽(五星)
对原型链条行的属性进行操作时,会屏蔽掉原型链条的属性。同时会给自己新增这个属性。
这个是隐士屏蔽
anther={a:2};
let myObj=Object.create(anther);
console.log(myObj.a);//2
myObj.a++;
console.log(anther.a);//2
console.log(myObj.a);//3
console.log(myObj.hasOwnProperty("a"));//true,同时新增了a属性
property的创建
property是写构造函数时就存在的。
new的时候目前我只能记忆起4件事:
1创建对象
2对象的原型链指向构造函数的原型
3绑定对象geithis
4如果没有返回值则返回this
constructor
是指向构造函数。
对象.constructor可以通过原型链找到。
如果原型={...},那么会通过原型链找到Object.property.constructor
如果原型={...}我们也可以通过Object.defineProperty...增加一个constructor属性
constructor是可修改的,只是不可枚举。
Object.create()为啥要用(五星)
Bar.prototype=Object.create(Foo.prototype);
vs
Bar.prototype=Foo.prototype
创建一个新对象,这个对象只有原型链__proto__指向 目标。例如像constructor是没有的。
是因为使用Object.create并不是直接引用Foo的原型,而是给Bar.prototype增加一个__proto__属性(原型链)指向Foo的原型。
那么以后在Bar的原型上增加方法是在Bar自己的原型上增加而不会改变Foo的原型,同时Foo的原型上的方法,通过原型链Bar又可以调用。
如果写成Bar.prototype=Foo.prototype那么以后对Bar的原型上的操作就会直接影响Foo.这样容易出现问题。
setPrototypeof可以替代Object.create()
Object.setPrototypeOf(旧的,新的)
这个方法比上面那个方法更丰富。
Object.create只是增加一个原型链的对象,没有constructor。但是使用Object.setPrototypeOf()则是在原先的对象中改变原型链指向目标对象。之前的constructor等内容都是存在的。并不是创建一个新的对象进行替代。
如下代码:
aaa不仅仅能访问到a,还能通过__proto__访问到b.
let aaa={a:"haha"};
let bbb={b:"bbbb"};
Object.setPrototypeOf(aaa,bbb);
console.log(aaa,bbb);
核心总结:Object.create是生成一个新的具有原型链的对象。setPrototypeOf是改变原型链指向。
instanceof只能判断对象和构造函数的关系
如果是两个对象,那么就不能够使用这个进行判断了。
instanceof其实无法真正的去判断对象是否是构造函数的实例。
如下实例:a并不是Foo的实例,但是却可以返回true:
let a={};
let b=Object.create(a);
function isIable(a,b){
function Foo(){}
Foo.prototype=a;
return b instanceof Foo;
}
console.log(isIable(a,b));//true,但是其实Foo和b并没有直接的关系。
核心总结:instanceof是判断对象的原型链是否指向函数的原型。但是用来判断实例是否属于构造函数是存在误差的。
Foo.prototype.isPrototypeOf()
这个方法判断的是对象的整个原型链条是否有包含Foo.prototype这个原型链。如果包含返回的是true,否则返回的是false。
这个方法其实就是我们上述内容自己写的isIable的方法。
Object.create()进一步理解
let a={};
console.log(a);//是个空对象,但是__proto__会指向Object.prototype
let b=Object.create(null);//不会有任何的原型链指向。用于存储数据不会受到其他链条的影响
console.log(b);
行为委托
对象关联
就是把需要的方法写在对象里边。如:
let obj={
a:function(){},
b:function(){}
}
let obj2=Object.create(obj);
//这样obj2就可以调用 obj1里边的方法。
并且obj2重写a这个方法也不会影响obj。
这个是非常重要的一点:Object.create();