JS面向对象的程序设计,大部分初学者写JS都是运用的面向过程的思维来写的(即一个function后接着另一个function)
其实JS中的function就是一个对象,如下:
var HelloWscats = function(){
console.log("wscats is not cat");
}
var _init = function(){
var obj = new HelloWscats();
}
_init(); // wscats is not cat
此时我们就可以调用_init方法打印 wscats is not cat ,它调用了HelloWscats的对象,当然这个HelloWscats对象没有任何属性和方法,它只有一个构造方法HelloWscats(),相当于调用了一个没有任何属性和方法的类,当使用new进行创建的时候,就调用了它的构造方法(也就是执行了HelloWscats里面的console.log("wscats is not cat");),当然现在很多JS的对象是直接定义了属性,而构造方法里面是没有其他可执行代码的。
其实HelloWscats就是我们所说的创建对象中的其中一种构造函数模式
var _init2 = function(param){
var obj = new Object();
var obj = {}
obj.action = "b";
Object.defineProperty(obj, "name", {
configurable: false,
writable: false,
value: "Wscats",
});
obj.name = "wscats"; //无效
delete obj.name; //无效
console.log("wscats is not cat");
return obj;
}
_init2("wscats is not cat");
我们稍微改一下,执行_init2()的时候,相当于创建对象中的另一种——工厂模式,创建对象交给一个工厂方法(_init2)来实现,可以传递参数。
这里创建对象的方式跟构造函数方式的区别在于:
- 构造函数能看到具体的定义对象类型HelloWscats,而工厂模式不能,因为这里工厂模式创建对象都是使用Object的原生构造函数来完成的。
本质是这两句代码结果的区别:
var obj = new HelloWscats(); //构造HelloWscats函数,并实例化HelloWscats对象
var obj = new Object(); //new一个Object对象,在工厂函数里面扩展该对象,并return该对象
再写一个简单的例子来说明他们的区别,如下:
var Constructor = function() {
console.log("wscats is not cat from constructor");
}
var objFromConstructor = new Constructor();
var Factory = function() {
var obj = new Object();
console.log("wscats is not cat from factory");
return obj;
}
var objFromFactory = Factory();
对于对象,肯定是要赋予它属性和方法的
我们可以用prototype在原型上进行赋值,比如我们要给HelloWscats对象增加一个name属性和say方法,那么我们就可以这样添加
var HelloWscats = function(){
this.action = "Code";
console.log("wscats is not cat");
}
HelloWscats.prototype = {
name: "wscats",
say: function(){
console.log("Wscats is not cat");
}
}
HelloWscats.prototype.skill = "Good Cat";
var _init = function(){
var obj = new HelloWscats();
obj.say();
console.log(obj);
}
_init();
这里我们看到,先是执行了构造函数中的代码,然后执行原型链中的代码。
这里需要注意的是,如果我们实例化了HelloWscats对象
var _init = function(){
var obj = new HelloWscats();
obj.say();
obj.action = "b";
console.log(obj);
}
我们可以改变这个对象的action属性,但我们没有办法改变原型链的对象,这个就类似于继承了HelloWscats对象的原型链的对象,继承的这些子类无法去更改,但是子类可以修改自己本身的属性和方法。
下面我们可以继续在构造函数里面写一个var privateVariables = "I am ur cat"; 来让HelloWscats拥有一个私有成员变量,当然这个变量也只有该对象的方法能访问,具体就是this.getPrivate函数能读到该私有变量的成员
var HelloWscats = function(){
var privateVariables = "I am ur cat";
this.getPrivate = function(){
console.log(privateVariables);
}
this.action = "Code";
console.log("wscats is not cat");
}
HelloWscats.prototype = {
name: "wscats",
say: function(){
console.log("Wscats is not cat");
}
}
HelloWscats.prototype.skill = "Good Cat";
var _init = function(){
var obj = new HelloWscats();
obj.say();
obj.action = "b";
obj.getPrivate();
console.log(obj);
}
_init();
它的巧妙之处其实在于这个构造函数的作用域,只能在类的构造函数里面进行访问该私有变量
上面我们一直用obj.name = "wscats" 或者 obj = {name: "wscats"}这两种方法来定义obj的属性,其实也可以使用defineProperty方法进行定义
Object.defineProperty(obj, "name", {
configurable: false,
writable: false,
value: "Wscats",
});
defineProperty方法接收三个参数:属性所在的对象,属性名和一个描述对象(必须是:configurable、enumberable、writable、value)
可以设置以下的数据类型(默认为true),这四个数据属性也是对象属性的默认四个数据属性:
- configurable:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性
- enumberable:表示是否可通过for-in循环返回属性
- writable:表示是否可修改属性的值
- value:包含该属性的数据值
如下,设置configurable为false,则修改name属性和删除name属性都变得无效
Objetc.defineProperty(obj, "name", {
configurable: false,
value: "Wscats",
})
obj.name = "wscats"; // 无效
delete obj.name; // 无效