JavaScript高级程序设计第3版总结p156

1.JavaScript中的对象

首先,ECMAScript 中函数实际上是对象。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。如此,根据ECMA-262 中对象的定义:“无序属性的集合,其属性可以包含基本值、对象或者函数。”,我们可以把 ECMAScript 的对象想象成散列表,一组名值对

创建自定义对象实例的方法有两种:一种是var person = new Object()再为其添加属性和方法person.name="Yann LeCun";另一种是通过对象字面量的方法var person = {name:"Yann LeCun",age:56},这里name就是对象实例person中的一个属性。

定义了对象的属性后,有时候还需要设置对象属性的属性,譬如前述对象实例personname属性是否可修改,若可修改,修改name属性时是否需要同时更新及如何更新age属性。

ECMAScript 中有两种属性描述了对象属性(property)的各种特征:数据属性访问器属性。(特性是内部值,ECMA-262规范把它们放在了两对儿方括号中)

  • 数据属性:配置对象属性属性的一些特征

    • [[Configurable]] :表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为 true。
    • [[Enumerable]] :表示能否通过 for-in 循环返回属性,默认值为 true。
    • [[Writable]] :表示能否修改属性的值,默认值为 true。
    • [[Value]] :包含这个属性的数据值,默认值为 undefined。

    要修改属性默认的特性,必须使用 ECMAScript 5 的 **Object.defineProperty() **方法,三个参数:属性所在的对象、属性的名字和一个描述符对象(configurable 、 enumerable 、 writable 和 value)

    Object.defineProperty(person, "name", {writable: false,value: "Nicholas"
    });
    

    注意:一旦configurable 设置为 false,再调用Object.defineProperty() 方法修改除 writable 之外的特性,都会导致错误。

  • 访问器属性:包含一对儿 getter 和 setter 函数。访问器属性有如下 4 个特性:

    • [[Configurable]] :同数据属性。
    • [[Enumerable]]:同数据属性。
    • [[Get]] :在读取属性时调用的函数,返回属性值。默认值为 undefined 。
    • [[Set]] :在写入属性时调用的函数,传入新值。默认值为 undefined 。
      访问器属性不能直接定义,必须使用 Object.defineProperty() 来定义。
    var book = {_year: 2004,edition: 1
    };
    Object.defineProperty(book, "year", {get: function(){return this._year;},set: function(newValue){if (newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}
    });
    book.year = 2005;
    console.log(book.edition);//2
    
  • Object.defineProperties(obj,{})同时定义多个属性,包括数据属性和访问器属性。

var book = {};
Object.defineProperties(book, {_year: {value: 2004},edition: {value: 1},year: {get: function(){return this._year;},set: function(newValue){if (newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}}
});
  • Object.getOwnPropertyDescriptor(book, “_year”)取得给定属性的描述符
var descriptor = Object.getOwnPropertyDescriptor(book, "_year");
console.log(descriptor.value);//2004
alert(descriptor.configurable); //false

2.如何创建对象

可以通过var obj = {key:value}对象字面量的方式来创建对象,这种方式简便直接,但若需要重复创建多个对象时就会很冗余(每个都要写一遍)。JS中创建对象有多种不同的模式,最基础的应该是工厂模式、构造函数模式和原型模式,基于这三种模式还衍生出了其他的如,原型构造函数组合模式、动态原型模式、寄生构造函数模式、稳妥构造函数模式,涉及到模式,更像是去理解一种设计思想,具有一定的抽象性。

  • 工厂模式
function createBook(name,year,edition){var obj = new Object();obj.name = name;obj.year = year; obj.edition = edition;obj.sayName = function(){console.log(this.name)}return obj;
}
var book1 = createBook('Master Javascript',2016,2);
var book2 = createBook('Master CSS',2016,2);

传入不同的参数返回不同的实例;单纯的工厂模式创建对象存在的主要问题是,无法进行对象识别,也就是不能自己创建类型。像上例中,book1book2是同一个类型,但是instanceof只能确定他们是object,而没有创建它们具体属于的类。这种感觉就像是说二哈和泰迪是动物,其实更具体来说他们是狗,但若没有狗这个类,那只能说他们是动物了。

  • 构造函数模式
function Book(name,year,edition){this.name = name;this.year = year;this.edition  = edition;this.sayName = function(){console.log(this.name)}
}
var book1 = new Book('Master Javascript',2016,2);
var book2 = new Book('Master CSS',2016,2);
console.log(book1 instanceof Book);//true
console.log(book1 instanceof Object);//true

1.首先看下thisjavascript中的this引用的是函数据以执行的环境对象.通过new来调用构造函数,将构造函数的作用域赋给新对象,所以构造函数的this就是指new出来的新对象,也就将传入的参数绑定到了新创建的对象上。
2.构造函数,函数名以大写字母开头(惯例)。
3.通过构造函数创建的对象可以确定具体类型book1就是Book类型。
4.构造函数与普通函数的唯一不同,就是调用方式不同,调用方式决定的是函数的作用域,任何函数通过new来调用也就都变成了构造函数。
5.上面的构造函数中定义了一个sayName方法,在JavaScript中,functionFunction类型的对象,所以上面的sayName方法还可写为this.sayName = new Function("alert(this.name)");,这样,每调用一次构造函数创建一个Book类型的实例,都会给每个实例开辟一片独立的内存空间创建一个sayName方法对象,而sayName完全可以定义成Book类型所有实例共享的方法,为每个实例创建一个同样的方法是奢侈浪费低性能的,这也正是构造函数模式存在问题,就是每个方法都要在每个
实例上重新创建一遍。
属于同一个类型的不同实例,不就应该既有一部分共享的,又有一部分个性化的属性和方法吗?

  • 原型模式
    每一个函数都有一个prototype的属性,这个属性是一个指向函数原型对象的指针。这个原型对象中包含了某个特定类型的所有实例共享的属性和方法

    function Book(){}
    Book.prototype.name = 'Master JavaScript';
    Book.prototype.year = 2019;
    Book.prototype.edition = 2;
    Book.prototype.sayName = function(){console.log(this.name);}
    var book1 = new Book();
    var book2 = new Book();
    book2.name = 'Master CSS';
    book2.year = 2019;
    book2.edition = 2;
    book2.sayName();//'Master CSS';
    book2.author = 'CSS Author';
    Book.prototype.isPrototypeOf(book1);//true
    Object.getPrototypeOf(book1) == Book.prototype
    delete book2.name;
    book2.name;//Master JavaScript
    book1.hasOwnProperty('name');//false;
    book2.hasOwnProperty('name');//true;
    ’name' in book1;//true
    //判断属性是存在于原型还是实例中
    function hasPrototypeProperty(object, name){return !object.hasOwnProperty(name) && (name in object);
    }
    Object.keys(book1);//[]
    Object.keys(book2);//["name", "year", "eidtion", "author"]
    Object.getOwnPropertyNames(Book.prototype);// ["constructor", "name", "year", "edition", "sayName"]
    

    1.JavaScript中,创建一个函数就会根据一组特定的规则为该函数创建一个prototype属性,指向函数的原型对象,语言如此设计,基于此实现对象创建继承等。刚创建函数时,函数的原型对象中都会自动包含prototype属性所在函数的指针Book.prototype.constructor.
    2.调用自定义的构造函数创建一个实例时,该实例内部包含一个指针[[prototype]],chrome等中每个实例都有一个__proto__属性,指向生成该实例的构造函数的原型对象,与构造函数已没有关系。
    3.isPrototypeOf(),判断某个构造函数的原型是不是某个实例的原型。Object.getPrototypeOf(),获取某个实例的__prototype所指的对象。
    4.访问实例属性时,先访问定义在实例上的属性,没有再去原型上搜索。在实例上新增的与原型中同名的属性,会屏蔽原型中定义的属性。使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性
    5. hasOwnProperty(),从Object中继承来的方法,当属性是在实例中定义的属性时,才会返回true
    6. in,in 操作符会在通过对象能够访问给定属性时返回 true
    7. 要取得对象上所有可枚举的实例属性,可以使用 ECMAScript 5 的 Object.keys()方法。
    8. Object.getOwnPropertyNames(),得到所有实例属性,无论它是否可枚举。
    9. 通过对象字面量方法创建原型对象会覆盖掉原型中constructo属性。手动创建constructor时,会在会导致它的 [[Enumerable]] 特性被设置为 true,可借助前述defineProperty设置其特性。
    10.先创建实例后通过对象字面量定义原型对象时,会出错,因为会重写原型对象,导致实例中的[[prototype]]属性无法指向创建的原型对象。
    11.原型对象存在问题,是由其共享的属性导致的,当原型属性中包含引用类型时,实例之间会相互影响

  • 构造函数和原型组合使用
    既然构造函数过于’个性‘,原型过于‘共享’,如果把同一类型实例共享的属性用原型定义,个性化的属性用构造函数来定义,问题不就解决了吗?构造函数与原型混成的模式,是目前在 ECMAScript 中使用最广泛、认同度最高的一种创建自定义类型的方法。

    function Book(name,year,edition){this.name = name;this.year = year;this.edition = edition;this.category = ['computer','software'];
    }
    Book.prototype = {consturctor:Book,sayName:function(){console.log(this.name);}
    }
    var book1 = new Book('Master JavaScript',2019,2);
    var book2 = new Book('Master CSS',2020,2);
    book1.category.push('JavaScript');//["computer", "software", "JavaScript"]
    book1.sayName == book2.sayName;//true
    book2.category;//["computer", "software"]
    
  • 动态原型模式
    根据需要,在构造函数中根据条件动态的初始化原型。

    function Book(name,year,edition){this.name = name;this.year = year;this.edition = edition;if(typeof this.sayName != "function"){Book.prototype.sayName = function(){console.log(this.name);}}}
    

    动态原型模式只通过构造函数来创建实例,更接近其他OO(面向对象)的语言,便于理解。

  • 寄生构造函数模式
    实现方式是,创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;

    function Book(name,year,edition){var obj =  new Object();obj.name = name;obj.year = year;obj.edition = edition;obj.sayName = function(){console.log(this.name);}return obj;
    }
    book1 = new Book('Master JavaScript',2019,2);
    book1 instanceof Book;//false
    

    寄生构造函数和工厂函数一样,区别在与调用方式,这里加了调用new操作符,Book函数的作用域赋值给了new出来的对象,通过返回值重写了这个new出来的对象,所以无法通过instanceof来识别对象。这个模式可以在特殊的情况下用来为对象创建构造函数。如特殊的数组。寄生可以理解为寄生在内部创建对象的类型上。

  • 稳妥构造函数
    稳妥对象,没有公共属性,不使用thisnew

    function Book(name,year,edition){var obj = {};obj.sayName:function(){console.log(name);}return obj;
    }
    var book = Book('Master CSS',2019,2);
    book.sayName();//'Master CSS'
    

    实现数据成员的私有,只能通过对象开放的方法访问其中的数据属性,安全性高。

其实,这两部分概括来说是,一部分是JavaScript对象属性相关的特性,一部分是创建对象的工厂函数、构造函数和原型方法,有时间补上几张图,条分缕析方便理解,加深印象。

JavaScript中的面向对象--对象创建相关推荐

  1. JavaScript中的面向对象--对象继承

    JavaScript高级程序设计第3版 p162 这里总结一下JavaScript中对象继承的方式,主要有原型链和借用构造函数模式,衍生的出来的有组合式继承.原型式继承.寄生式继承和寄生组合式继承.原 ...

  2. Javascript基础与面向对象基础~第四讲 Javascript中的类对象

    今天来说JS中如何实现类(class),事实上本应该昨天晚上写的,可我失言了,在些说一声"抱歉"!JS中的类是JS面向对象的基础,也是我最拿手的东西,你写的代码能否提高一个层次,一 ...

  3. 如何理解并学习javascript中的面向对象(OOP)

    本文不适合javascript初学者看(javascript水平还停留在函数级别的朋友,看了会觉得很晕的).如果你想让你的javascript代码变得更加优美,性能更加卓越.或者,你想像jQuery的 ...

  4. javascript 中的面向对象实现 如何封装

    javascript 是一门很灵活的语言,也是一门有缺陷的语言. 比如我们今天要谈的,如何用面向对象的手法来封装javascript ,javascript是没有类的概念的. 所以今天谈到的封装,其实 ...

  5. html5学习笔记---05.JavaScript 中的面向对象,继承和封装

    05.JavaScript 中的面向对象 a.创梦技术qq交流群:CreDream:251572072 a.JavaScript 是一种基于对象的语言   类:JavaScript 对象很抽象,所以下 ...

  6. javascript中的面向对象理解(一)

    一.注意:提到"面向对象"这一概念,众所周知,javascript中的面向对象思想与其他的编程语言(例如:PHP.Java等)是有着很大区别的.因此,我们先复习下,传统意义上,面向 ...

  7. 第163天:js面向对象-对象创建方式总结

    面向对象-对象创建方式总结 1. 创建对象的方式,json方式 推荐使用的场合: 作为函数的参数,临时只用一次的场景.比如设置函数原型对象. 1 var obj = {}; 2 //对象有自己的 属性 ...

  8. 【从0到1学Web前端】javascript中的ajax对象(一)

    [从0到1学Web前端]javascript中的ajax对象(一) 如今最流行的获取后端的(浏览器从server)数据的方式就是通过Ajax了吧.今天就来具体的来学习下这个知识吧.假设使用ajax来訪 ...

  9. php节点对象,JavaScript_JavaScript中访问节点对象的方法有哪些如何使用,JavaScript中访问节点对象的方法 - phpStudy...

    JavaScript中访问节点对象的方法有哪些如何使用 JavaScript中访问节点对象的方法有哪些? var obj = document.getElementById('fdafda'); va ...

最新文章

  1. 《C++覆辙录》——2.9:自反初始化
  2. android开发入门_Android开发入门
  3. [傅里叶变换及其应用学习笔记] 九. 继续卷积的讨论
  4. idea 自动生成mybaits_IDEA利用mybatis-generator自动生成dao和mapper
  5. 深入学习Java多线程——并发机制底层实现原理
  6. 高并发Redis缓存如何设计
  7. [POI2009]SLO
  8. fullPage.js插件用法(转发)
  9. 每天工作4小时的程序员_IT新闻_博客园
  10. Atiitt 程序语言vm与rt 虚拟机与运行时 目录 1. 运行时 虚拟机的一种,一般指进程级别的虚拟机。 1 1.1. 线程模型 1 1.2. 堆栈机vs 寄存器 1 1.3. 存储模型 2 1
  11. 计算机数字媒体学什么以后,数字媒体设计是学什么的?以后的发展方向是什么?...
  12. UTM投影的选择(地区-投影带)
  13. 使用octotree 出现Error: Connection error octotree解决办法
  14. 软件工程实训有必要吗_软件工程专业有没有必要考研?
  15. ftp服务器项目,ftp服务器项目手册.doc
  16. python中inf_认识python中的inf和nan
  17. 分享一个微信扫码连wifi项目
  18. iterm2连不上阿里云服务器
  19. linux下编译Zero C ICE
  20. 数据结构与算法--第二章pro题解

热门文章

  1. 2018ACM上海大都会赛: A. Fruit Ninja(这绝对是道原题+随机)
  2. 经典问题8连:小球和盒子
  3. javascript之字符串常用方法学习 charAt concat indexOf substring substr toUpperCase
  4. Echarts数据可视化legend图例,开发全解+完美注释
  5. hls和modelsim进行联合仿真
  6. shell的简单应用
  7. JAVA 遍历文件夹下的所有文件
  8. Testing a React+Redux web application
  9. JSP的优势与劣势浅析
  10. Linux搭建SVN 服务器(转)