@FunC
2017-03-04T22:01:56.000000Z
字数 4658
阅读 2529
JavaScript
.hasOwnProperty
配合in
操作符)插播一则ES6更新
class中的方法前加上static
关键字,成为静态方法。只能在class上调用,不能在实例中调用。
与ES5构建的“类”的区别:
使用extends
关键字
constructor中使用super
关键字使this
绑定到父类的实例
1.创建单个对象
//早期方法,现在很少用
var person = new Object();
person.name = "John";
//对象字面量语法,直观
var person = {
name: "John"
}
存在问题:当需要创建多个对象时,要写大量重复代码
2.创建多个对象
2.1工厂模式
将创建对象的过程封装成一个函数
function createPerson(name){
var o = New Object();
o.name = name
return o;
}
var person1 = createPerson("John");
var person2 = createPerson("Mike");
工厂模式的问题:创建的对象没有其类型的名字,所以无法判断两个对象是否为同一个类型
2.2构造函数模式
为了解决对象类型的问题,可使用构造函数来创建对象,该方法的核心在于使用了new
操作符
来看看对函数使用new
操作符会发生什么
- 创建一个新的对象
- 将构造函数的作用域赋给新对象(因此
this
指向了这个新对象)- 执行构造函数中的代码
- 返回创建的新对象
于是我们就可以把构造函数写成如下形式:
function Person(name){
this.name = name;
this.sayName = function(){
alert(this.name);
}
}
var person1 = new Person("John");
var person2 = new Person("Mike");
注意其实不用new
操作符这个函数也能跑,只是this
指针就指向了windows
对象了;为了表明这是我们定义的一种对象类型,函数名的首字母要大写(毕竟关键字都是function
,容易和一般的函数混淆)
这样,person1和person2分别保存着Person的一个不同的实例,而且两个对象都有一个constructor属性,该属性指向构造函数Person,如下所示:
alert(person1.constructor == Person);//true
alert(person2.constructor == Person);//true
构造函数的问题:
直接看例子
console.log(person1.sayName == person2.sayName);//false
可以看到,用上述方法定义的方法是两个不同的对象实例
this.sayName = New Function(console.log(this.name));//这种写法等价于上述写法
然而这个方法的功能是一样的,创建不同的实例是没有必要的。所以调用方法时,其实只要找到对应的函数即可,而不是调用时创建一个函数。那么我们其实可以把函数写在构造函数外面:
function Person(name){
//...
this.sayName = sayName;
}
function sayName(){
console.log(this.Name);
}
新的问题来了:这些定义在全局作用域的函数只能被某些对象使用,似乎失去了全局作用域的意义。另一方面,各种构造函数的方法都混杂着定义在全局当中,对构造函数来说毫无封装性可言。
2.3原型模式
每一个函数都有prototype属性,属性值是指向其原型对象的指针(对应的原型对象有constructor属性指向构造函数)。该原型对象的属性和方法由所有实例共享。
所以推荐把方法定义在原型上
注意:如使用对象字面量改写原型:
Person.prototype = {
this.name = name;
}
实质上变更了Person.prototype指针的指向,而不是在原来的基础上修改,因此也丢失了constructpr属性。
综上,创建自定义类型时,建议方法定义在原型上,属性定义在构造函数当中。
加强版(定义在原型上的方法也写进构造函数中):
function Person(name, age, job){
//属性
this.name = name;
this.age = age;
this.job = job;
//方法
//含多个方法时也只需要一条if语句,因为能通过表示初始化成功
if( type of this.sayName != 'function'){
Person.prototype.sayName = function(){
alert(this.name);
}
}
}
2.5寄生构造函数模式(其实是工厂模式,但可以用new
)
试想我们要构建一个具有特殊功能的数组对象,要怎么做?
- 不建议修改原生对象的原型。在不同环境下可能发生明明冲突;还有可能意外重写原生方法。
function specialArray(){
//创建数组
var values=new Array();
//添加值
//注意这里用apply是为了把arguments这个伪数组拆开,否则等于把arguments这个对象整体作为values数组的一项
//ES6中可以用拓展运算符代替,更直观,即values.push(...arguments);
values.push.apply(values,arguments);
//添加方法
values.toPipedstring=function(){
return this.join("|");
}
//返回数组
return values;
}
var colors=new specialArray('red','yellow','green');
alert(colors.toPipedstring()); // red|yellow|green
其实就是可用new的工厂模式,工厂模式的缺点它都有,用instanceof运算符也只能返回Object,尽量不建议使用这种模式;
2.6稳妥构建函数模式(安全)
稳妥对象:即无公共属性,同时其方法不引用this的对象。
即传入参数直接用于构建方法,不在属性上停留。如:
function Person(name){
var o = new Object();
0.sayName = function(){
alert(name);
};
return o;
}
试想一下,如果用原型A的实例来作为构造函数B的原型会发生什么?
没错,就是实例B拥有了原型A的属性和方法。
因为这涉及到指针操作,所以要注意赋值操作的顺序
直接看代码吧:
function SuperType(namne){
this.name = name;
}
function SubType(){
//把this传递过去相当于把上面的代码复制了过来
SuperType.call(this, "Nicholas");
//实例属性
this.age = 29;
}
还是构造函数的老问题,方法不共用
即属性用构造函数继承,方法通过原型链继承
//超类型方法的定义
SuperType.prototype.sayName = function(){
alert(this.name);
}
//子类型继承方法
SubType.prototype = new SuperType();
//子类型定义方法
SubType.prototype.sayAge = alert(this.age);
思想是用一个实例函数作为另一个对象的原型。
ES5中提供了方法Object.create()
来实现,第一个参数为用作原型的实例对象,第二个参数(可忽略)为额外的属性值,需要同时写上属性描述符;
即用函数把定义自己的方法这一操作打包起来,同样方法不能共享。
细心的话可以发现,上述的组合继承中在原型中继承的属性是多余的
实现方法如下:
function inheritPrototype(subType, SuperType){
//浅复制超类型的原型(即只含有方法)
var prototype = Object.create(SuperType.prototype);
//将刚才浅复制的原型的constructor指向subType
prototype.constructor = subType;
//subType的prototype指向上面复制的原型
subType.prototype = prototype;
}
var o = new Object();
与var o = {};
等价。
Object()
本身也是一个工具方法,作用是:将任意值转为对象。
该方法常用于保证某个值一定是对象。
如果参数是原始类型的值,Object方法返回对应的包装对象的实例(参见《原始类型的包装对象》一节)。
Object对象的静态方法指的是部署在Object
这个对象上的方法(非实例对象)。
- Object.keys()
- Object.getOwnPropertyNames()
Object.keys()
更常用,用于返回可枚举的属性。
Object.getOwnPropertyNames()
返回可枚举和不可枚举的属性(例如数组中的.length
)
- Object.getOwnPropertyDescriptor():获取某个属性的attributes对象。
- Object.defineProperty():通过attributes对象,定义某个属性。
- Object.defineProperties():通过attributes对象,定义多个属性。
- Object.getOwnPropertyNames():返回直接定义在某个对象上面的全部属性的名称。
- Object.preventExtensions():防止对象扩展。
- Object.isExtensible():判断对象是否可扩展。
- Object.seal():禁止对象配置。
- Object.isSealed():判断一个对象是否可配置。
- Object.freeze():冻结一个对象。
- Object.isFrozen():判断一个对象是否被冻结。