Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript面向对象编程 #32

Closed
Wscats opened this issue Jun 20, 2016 · 4 comments
Closed

Javascript面向对象编程 #32

Wscats opened this issue Jun 20, 2016 · 4 comments
Labels

Comments

@Wscats
Copy link
Owner

Wscats commented Jun 20, 2016

JS面向对象的程序设计,JS在很早以前一直被认为毫无章法的语言,大部分初学者写的时候都是运用面向过程的思维来编写JS的(一页下来就是一个function接着另一个function的写法)

其实JS中function就是一个对象,如下面的代码

var HelloWscats = function(){
    console.log("wscats is not cat");
}

var _init = function(){
    var obj = new HelloWscats();
}

_init();//log: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();

这里我们看到了先是执行了构造函数中的console.log("wscats is not cat");,然后在执行原型链中的console.log("Wscats is not cat");
打开浏览器看看打印的obj对象具体是怎么样的
image

上面有一定注意的是,如果我实例化了HelloWscats对象

var _init = function(){
    var obj = new HelloWscats();
    obj.say();
    obj.action = "b";
    console.log(obj);
}

我们可以改变,这个对象的action属性obj.action = "b";,但我们没办法改变原型链的对象,这个就类似于继承了HelloWscats对象的原型链的对象,继承的这些子类无法更改,但是子类可以修改自己本身的属性和方法

var HelloWscats = function(){
    var privateVvariables = "I am ur cat";
    this.getPrivate = function(){
        console.log(privateVvariables);
    }
    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();

下面我可以继续在构造函数里面写一个var privateVvariables = "I am ur cat";来让HelloWscats拥有一个私有成员变量,当然这个变量也只有该对象的方法能访问,具体就是this.getPrivate函数能读到该私有变量的成员

this.getPrivate = function(){
    console.log(privateVvariables);
}

它的巧妙之处其实在于这个构造函数的作用域,只能在类的构造函数里面进行访问该私有变量

var _init = function() {
    var obj = new HelloWscats();
    obj.say();
    obj.action = "b";
    obj.getPrivate();
    Object.defineProperty(obj, "name", {
        configurable: false,
        writable: false,
        value: "Wscats",
    });
    obj.name = "wscats";//无效
    delete obj.name;//无效
    console.log(obj);
}

上面我们一直用obj.name = "wscats"或者obj = {name: "wscats"}这两种方法来编辑或者定义obj的属性,那么有没有其他更好的方法呢,答案是有的,正如上面的这一段代码

Object.defineProperty(obj, "name", {
    configurable: false,
    writable: false,
    value: "Wscats",
});

defineProperty方法能修改对象的属性它接收三个参数:属性所在对象,属性名和一个描述符对象(必须是:configurable、enumberable、writable和value,可设置一个或多个值)
他可以设置以下的数据属性(他们默认都为true),这四个数据属性也是对象属性的默认四个数据属性:

  1. configurable:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性。默认为true;
  2. Enumberable: 表示是否可通过for-in循环返回属性。默认true;
  3. Writable: 表示是否可修改属性的值。默认true;
  4. Value: 包含该属性的数据值。读取/写入都是该值

正如我写obj的name属性configurable: false,writable: false,

obj.name = "wscats";//无效
delete obj.name;//无效

删除该对象的name属性值和修改name属性值也变得无效

@Wscats
Copy link
Owner Author

Wscats commented Jun 21, 2016

😢

@Wscats
Copy link
Owner Author

Wscats commented Jun 21, 2016

上面HelloWscats()构造函数中的getPrivate方法,当我们每一次实例化的时候var obj = new HelloWscats();都会造成创建多个函数实例,这会消耗内存资源,所以我们可以用原型模式prototype(原型)属性来改善,这个属性是一个指针,指向一个对象,它是所有通过new操作符使用函数创建的实例的原型对象。

this.getPrivate = function() {
    console.log(privateVvariables);
}

原型对象最大特点是,所有对象实例共享它所包含的属性和方法,也就是说,所有在原型对象中创建的属性或方法都直接被所有对象实例共享

@ezjason
Copy link

ezjason commented Mar 29, 2017

nice 半脸懵逼

@luyaoguang
Copy link

getPrivate在原型对象里面怎么访问你在构造函数里定义的privateVvariables?

@Wscats Wscats closed this as completed Aug 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants