JavaScript中的面向对象--对象创建
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中的一个属性。
定义了对象的属性后,有时候还需要设置对象属性的属性,譬如前述对象实例person的name属性是否可修改,若可修改,修改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);
传入不同的参数返回不同的实例;单纯的工厂模式创建对象存在的主要问题是,无法进行对象识别,也就是不能自己创建类型。像上例中,book1
和book2
是同一个类型,但是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.首先看下this
,javascript
中的this引用的是函数据以执行的环境对象.通过new
来调用构造函数,将构造函数的作用域赋给新对象,所以构造函数的this
就是指new
出来的新对象,也就将传入的参数绑定到了新创建的对象上。
2.构造函数,函数名以大写字母开头(惯例)。
3.通过构造函数创建的对象可以确定具体类型,book1
就是Book
类型。
4.构造函数与普通函数的唯一不同,就是调用方式不同,调用方式决定的是函数的作用域,任何函数通过new
来调用也就都变成了构造函数。
5.上面的构造函数中定义了一个sayName
方法,在JavaScript
中,function
是Function
类型的对象,所以上面的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
来识别对象。这个模式可以在特殊的情况下用来为对象创建构造函数。如特殊的数组。寄生可以理解为寄生在内部创建对象的类型上。稳妥构造函数
稳妥对象,没有公共属性,不使用this
和new
。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中的面向对象--对象创建相关推荐
- JavaScript中的面向对象--对象继承
JavaScript高级程序设计第3版 p162 这里总结一下JavaScript中对象继承的方式,主要有原型链和借用构造函数模式,衍生的出来的有组合式继承.原型式继承.寄生式继承和寄生组合式继承.原 ...
- Javascript基础与面向对象基础~第四讲 Javascript中的类对象
今天来说JS中如何实现类(class),事实上本应该昨天晚上写的,可我失言了,在些说一声"抱歉"!JS中的类是JS面向对象的基础,也是我最拿手的东西,你写的代码能否提高一个层次,一 ...
- 如何理解并学习javascript中的面向对象(OOP)
本文不适合javascript初学者看(javascript水平还停留在函数级别的朋友,看了会觉得很晕的).如果你想让你的javascript代码变得更加优美,性能更加卓越.或者,你想像jQuery的 ...
- javascript 中的面向对象实现 如何封装
javascript 是一门很灵活的语言,也是一门有缺陷的语言. 比如我们今天要谈的,如何用面向对象的手法来封装javascript ,javascript是没有类的概念的. 所以今天谈到的封装,其实 ...
- html5学习笔记---05.JavaScript 中的面向对象,继承和封装
05.JavaScript 中的面向对象 a.创梦技术qq交流群:CreDream:251572072 a.JavaScript 是一种基于对象的语言 类:JavaScript 对象很抽象,所以下 ...
- javascript中的面向对象理解(一)
一.注意:提到"面向对象"这一概念,众所周知,javascript中的面向对象思想与其他的编程语言(例如:PHP.Java等)是有着很大区别的.因此,我们先复习下,传统意义上,面向 ...
- 第163天:js面向对象-对象创建方式总结
面向对象-对象创建方式总结 1. 创建对象的方式,json方式 推荐使用的场合: 作为函数的参数,临时只用一次的场景.比如设置函数原型对象. 1 var obj = {}; 2 //对象有自己的 属性 ...
- 【从0到1学Web前端】javascript中的ajax对象(一)
[从0到1学Web前端]javascript中的ajax对象(一) 如今最流行的获取后端的(浏览器从server)数据的方式就是通过Ajax了吧.今天就来具体的来学习下这个知识吧.假设使用ajax来訪 ...
- php节点对象,JavaScript_JavaScript中访问节点对象的方法有哪些如何使用,JavaScript中访问节点对象的方法 - phpStudy...
JavaScript中访问节点对象的方法有哪些如何使用 JavaScript中访问节点对象的方法有哪些? var obj = document.getElementById('fdafda'); va ...
最新文章
- 《C++覆辙录》——2.9:自反初始化
- android开发入门_Android开发入门
- [傅里叶变换及其应用学习笔记] 九. 继续卷积的讨论
- idea 自动生成mybaits_IDEA利用mybatis-generator自动生成dao和mapper
- 深入学习Java多线程——并发机制底层实现原理
- 高并发Redis缓存如何设计
- [POI2009]SLO
- fullPage.js插件用法(转发)
- 每天工作4小时的程序员_IT新闻_博客园
- Atiitt 程序语言vm与rt 虚拟机与运行时 目录 1. 运行时 虚拟机的一种,一般指进程级别的虚拟机。	1 1.1. 线程模型	1 1.2. 堆栈机vs 寄存器	1 1.3. 存储模型	2 1
- 计算机数字媒体学什么以后,数字媒体设计是学什么的?以后的发展方向是什么?...
- UTM投影的选择(地区-投影带)
- 使用octotree 出现Error: Connection error octotree解决办法
- 软件工程实训有必要吗_软件工程专业有没有必要考研?
- ftp服务器项目,ftp服务器项目手册.doc
- python中inf_认识python中的inf和nan
- 分享一个微信扫码连wifi项目
- iterm2连不上阿里云服务器
- linux下编译Zero C ICE
- 数据结构与算法--第二章pro题解
热门文章
- 2018ACM上海大都会赛: A. Fruit Ninja(这绝对是道原题+随机)
- 经典问题8连:小球和盒子
- javascript之字符串常用方法学习 charAt concat indexOf substring substr toUpperCase
- Echarts数据可视化legend图例,开发全解+完美注释
- hls和modelsim进行联合仿真
- shell的简单应用
- JAVA 遍历文件夹下的所有文件
- Testing a React+Redux web application
- JSP的优势与劣势浅析
- Linux搭建SVN 服务器(转)