zoukankan      html  css  js  c++  java
  • JavaScript 面向对象

    • 对象具有唯一标识性:即使完全相同的两个对象,也并非同一个对象。

    • 对象有状态:对象具有状态,同一对象可能处于不同状态之下。

    • 对象具有行为:即对象的状态,可能因为它的行为产生变迁。

    var o1 = { a: 1 };
    var o2 = { a: 1 };
    console.log(o1 == o2); // false
    

    关于对象的第二个和第三个特征“状态和行为”,不同语言会使用不同的术语来抽象描述它们,比如C++中称它们为“成员变量”和“成员函数”Java中则称它们为“属性”和“方法”

    在 JavaScript中,将状态和行为统一抽象为“属性”,考虑到 JavaScript 中将函数设计成一种特殊对象,所以 JavaScript中的行为和状态都能用属性来抽象。

    var o = { 
       d: 1,
       f() {
            console.log(this.d);
        }    
    };
    

    所以,总结一句话来看,在JavaScript中,对象的状态和行为其实都被抽象为了属性。如果你用过Java,一定不要觉得奇怪,尽管设计思路有一定差别,但是二者都很好地表现了对象的基本特征:标识性、状态和行为。

    JavaScript创始人Brendan Eich(布兰登·艾奇)在“原型运行时”的基础上引入了new、this等语言特性,使之“看起来语法更像Java”,而Java正是基于类的面向对象的代表语言之一。

    什么是原型?

    原型是顺应人类自然思维的产物。中文中有个成语叫做“照猫画虎”,这里的猫看起来就是虎的原型,所以,由此我们可以看出,用原型来描述对象的方法可以说是古已有之。

    在不同的编程语言中,设计者也利用各种不同的语言特性来抽象描述对象。

    最为成功的流派是使用“类”的方式来描述对象,这诞生了诸如 C++、Java等流行的编程语言。这个流派叫做基于类的编程语言。

    还有一种就是基于原型的编程语言,它们利用原型来描述对象。我们的JavaScript就是其中代表

    “基于类”的编程提倡使用一个关注分类和类之间关系开发模型。在这类语言中,总是先有类,再从类去实例化一个对象。类与类之间又可能会形成继承、组合等关系。类又往往与语言的类型系统整合,形成一定编译时的能力。

    与此相对,“基于原型”的编程看起来更为提倡程序员去关注一系列对象实例的行为,而后才去关心如何将这些对象,划分到最近的使用方式相似的原型对象,而不是将它们分成类。

    基于原型的面向对象系统通过“复制”的方式来创建新对象。一些语言的实现中,还允许复制一个空对象。这实际上就是创建一个全新的对象。

    基于原型和基于类都能够满足基本的复用和抽象需求,但是适用的场景不太相同。

    这就像专业人士可能喜欢在看到老虎的时候,喜欢用猫科豹属豹亚种来描述它,但是对一些不那么正式的场合,“大猫”可能更为接近直观的感受一些(插播一个冷知识:比起老虎来,美洲狮在历史上相当长时间都被划分为猫科猫属,所以性格也跟猫更相似,比较亲人)。

    我们的JavaScript 并非第一个使用原型的语言,在它之前,self、kevo等语言已经开始使用原型来描述对象了。

    事实上,Brendan更是曾透露过,他最初的构想是一个拥有基于原型的面向对象能力的scheme语言。

    原型系统的“复制操作”有两种实现思路:

    一个是并不真的去复制一个原型对象,而是使得新对象持有一个原型的引用;

    另一个是切实地复制对象,从此两个对象再无关联。

    历史上的基于原型语言因此产生了两个流派,显然,JavaScript显然选择了前一种方式。

    JavaScript的原型

    如果我们抛开JavaScript用于模拟Java类的复杂语法设施(如new、Function Object、函数的prototype属性等),原型系统可以说相当简单,我可以用两条概括:

    如果所有对象都有私有字段[[prototype]],就是对象的原型;
    读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。
    这个模型在ES的各个历史版本中并没有很大改变,但从 ES6 以来,JavaScript提供了一系列内置函数,以便更为直接地访问操纵原型。三个方法分别为:

    Object.create 根据指定的原型创建新对象,原型可以是null;
    Object.getPrototypeOf 获得一个对象的原型;
    Object.setPrototypeOf 设置一个对象的原型。

    利用这三个方法,我们可以完全抛开类的思维,利用原型来实现抽象和复用。我用下面的代码展示了用原型来抽象猫和虎的例子。

    var cat = {
        say(){
            console.log("meow~");
        },
        jump(){
            console.log("jump");
        }
    }
    
    var tiger = Object.create(cat,  {
        say:{
            writable:true,
            configurable:true,
            enumerable:true,
            value:function(){
                console.log("roar!");
            }
        }
    })
    
    
    var anotherCat = Object.create(cat);
    
    anotherCat.say();
    
    var anotherTiger = Object.create(tiger);
    

    这段代码创建了一个“猫”对象,又根据猫做了一些修改创建了虎,之后我们完全可以用Object.create来创建另外的猫和虎对象,我们可以通过“原始猫对象”和“原始虎对象”来控制所有猫和虎的行为。

    new 运算接受一个构造器和一组调用参数,实际上做了几件事:

    以构造器的 prototype 属性(注意与私有字段[[prototype]]的区分)为原型,创建新对象;
    将 this 和调用参数传给构造器,执行;
    如果构造器返回的是对象,则返回,否则返回第一步创建的对象。
    new 这样的行为,试图让函数对象在语法上跟类变得相似,但是,它客观上提供了两种方式,一是在构造器中添加属性,二是在构造器的 prototype 属性上添加属性。

    下面代码展示了用构造器模拟类的两种方法:

    function c1(){
        this.p1 = 1;
        this.p2 = function(){
            console.log(this.p1);
        }
    } 
    var o1 = new c1;
    o1.p2();
    
    function c2(){
    }
    c2.prototype.p1 = 1;
    c2.prototype.p2 = function(){
        console.log(this.p1);
    }
    
    var o2 = new c2;
    o2.p2();
    

    第一种方法是直接在构造器中修改this,给this添加属性。

    第二种方法是修改构造器的prototype属性指向的对象,它是从这个构造器构造出来的所有对象的原型。

    比起早期的原型模拟方式,使用extends关键字自动设置了constructor,并且会自动调用父类的构造函数,这是一种更少坑的设计。

    所以当我们使用类的思想来设计代码时,应该尽量使用class来声明类,而不是用旧语法,拿函数来模拟对象。

    JavaScript中的对象分类

    我们可以把对象分成几类。

    • 宿主对象(host Objects):由JavaScript宿主环境提供的对象,它们的行为完全由宿主环境决定。

    • 内置对象(Built-in Objects):由JavaScript语言提供的对象。

    • 固有对象(Intrinsic Objects ):由标准规定,随着JavaScript运行时创建而自动创建的对象实例。

    • 原生对象(Native Objects):可以由用户通过Array、RegExp等内置构造器或者特殊语法创建的对象。

    • 普通对象(Ordinary Objects):由{}语法、Object构造器或者class关键字定义类创建的对象,它能够被原型继承。

    普通对象之外的对象类型

    宿主对象

    JavaScript宿主对象千奇百怪,但是前端最熟悉的无疑是浏览器环境中的宿主了。

    在浏览器环境中,我们都知道全局对象是window,window上又有很多属性,如document。

    实际上,这个全局对象window上的属性,一部分来自JavaScript语言,一部分来自浏览器环境。

    JavaScript标准中规定了全局对象属性,w3c的各种标准中规定了Window对象的其它属性。

    宿主对象也分为固有的和用户可创建的两种,比如document.createElement就可以创建一些dom对象。

    宿主也会提供一些构造器,比如我们可以使用new Image来创建img元素,这些我们会在浏览器的API部分详细讲解。

    内置对象·固有对象

    固有对象是由标准规定,随着JavaScript运行时创建而自动创建的对象实例。

    固有对象在任何JS代码执行前就已经被创建出来了,它们通常扮演者类似基础库的角色。我们前面提到的“类”其实就是固有对象的一种。

    ECMA标准为我们提供了一份固有对象表,里面含有150+个固有对象。

    内置对象·原生对象

    我们把JavaScript中,能够通过语言本身的构造器创建的对象称作原生对象。在JavaScript标准中,提供了30多个构造器。按照我的理解,按照不同应用场景,我把原生对象分成了以下几个种类。

    通过这些构造器,我们可以用new运算创建新的对象,所以我们把这些对象称作原生对象。
    几乎所有这些构造器的能力都是无法用纯JavaScript代码实现的,它们也无法用class/extend语法来继承。

    这些构造器创建的对象多数使用了私有字段,例如:

    Error: [[ErrorData]]
    Boolean: [[BooleanData]]
    Number: [[NumberData]]
    Date: [[DateValue]]
    RegExp: [[RegExpMatcher]]
    Symbol: [[SymbolData]]
    Map: [[MapData]]
    这些字段使得原型继承方法无法正常工作,所以,我们可以认为,所有这些原生对象都是为了特定能力或者性能,而设计出来的“特权对象”。

    原文摘自 winter

  • 相关阅读:
    http header Contenttype
    SharePoint 2010 文档库中直接打开文档
    玩转Google开源C++单元测试框架Google Test系列(gtest)(总)
    最近感兴趣的二三事
    最近遭遇的两个VS配置
    环游世界,走遍读过的每一个国家和城镇
    趣题一则:如何快速过桥?
    NASA庆祝地球日:50年地球最精美图片亮相(转载)
    Silverlight,Windows 8应用开发实例教程系列汇总
    Windows 8实例教程系列 数据绑定高级实例
  • 原文地址:https://www.cnblogs.com/lisaShare/p/11008120.html
Copyright © 2011-2022 走看看