从头认识js-js中的对象
什么是对象?
ECMA-262中把对象定义为:“无序属性的集合,其属性可以包含基本值,对象或者函数”。严格来讲,对象是一组没有特定顺序的值。对象的每个属性或方法·都有一个名字,而每个名字都映射到一个值。
属性类型
1.数据属性
1.[[Configurable]]:表示能够通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
2.[[Enumerable]]:表示能否通过for-in循环返回属性。
3.[[Writable]]:表示能否修改属性的值。
4.[[Value]]:包含这个属性的数据值。读取属性的值的时候,从这个位置读取;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefiend。
要修改属性默认的特性,必须使用ECMAScript5的Object.defineProperty()方法。接受三个参数:属性所在的对象,属性的名字和一个描述符对象(descriptor 对象的属性必须是:configurable,enumerable,writable和value)设置一个或者多个值,可以修改对应的特性。
注意:可以多次调用该方法修改同一个属性,但是把configurable特性设置为false之后就会有限制了。
2.访问器属性
访问器属性不包含数据值:它们包含一对儿getter和setter函数。
1.[[Configurable]]:表示能够通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。
2.[[Enumerable]]:表示能否通过for-in循环返回属性。
3.[[Get]]:在读取属性时调用的函数。默认值为undefined。
4.[[Set]]:在写入属性是调用的函数。默认值为undefeated。
3.定义多个属性
使用Object.defineProperties()方法,可以通过描述符一次定义多个属性。该方法接受两个参数:第一个参数是一个添加或者修改属性的对象,第二个参数是一个对象,该对象的属性与第一个对象中添加或修改的属性一一对应。
4.读取属性的特性
使用Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符,该方法接受两个参数:属性所在的对象和读取去描述符的属性名称。返回值是一个对象,如果是访问器属性,这个对象的属性有configurable,enumerable,get和set;如果是数据属性,这个对象的属性有configurable,enumerable,writable和value。
创建对象
1.工厂模式
function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sauName = function () {
console.log(this.name);
}
return o;
}
var person1 = createPerson('小明', 22, '学生');
var person2 = createPerson('小红', 27, '白领');
函数createPerson每个可以返回一个包含三个属性的对象。这就是典型的工厂模式,工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
2.构造函数模式
像Object和Array的原生构造函数,在运行时会自动出现在执行环境中。此外,我们也可以创建自定义的构造函数,从而定义自定义对象的属性和方法。现在我们使用构造函数模式重写上一个例子。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
}
} var person1 = new Person('a', 22, '学生');
var person2 = new Person('b', 23, '白领');
我们注意到,Person函数相对以createPerson函数,存在以下不同:
1.没有显示地创建对象。
2.直接将属性和方法赋给了this对象。
3.没有return语句。
Person函数就是一个构造函数,注意到函数Person的首字母是大写的,可以用来创建对象,这也是构造函数区别于其他函数的特征。
要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:
1.创建一个新对象。
2.将构造函数的作用域赋予新对象(因此this指向了这个新对象)。
3.执行构造函数中的代码(为这个新对象添加属性和方法)。
4.返回新对象。
person1和person2分别保存着Person的一个不同的实例。这两个对象有一个constructor(构造函数)属性,该属性指向Person。
对象的constructor属性最初是用来标识对象类型的。
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,这也是构造函数模式胜于工厂模式的地方。
构造函数的问题:使用构造函数的主要问题,就是在每个方法都要在每个实例上重新创建一遍。
3.原型模式
每一个函数都有一个propertype(原型)属性,该属性是一个指针,指向一个对象,这个对象的用途是包含可以有特定类型的所有实例共享的属性和方法。如果按照字面量来理解,那么propertype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处就是是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
1.理解原型对象
创建函数就会为该函数创建一个propertype属性,这个属性指向函数的原型对象。默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性指向propertype属性所在函数的指针。
创建了自定义的构造函数后,其原型对象默认只会取得constructor属性;至于其他方法,则都是从Object继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262第5版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方法访问[[Prototype]],但在Firefox,Safari和Chrome在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性是对脚本完全不可见的。注意的是这个连接存在于实例于构造函数的原型对象之间,而不是存在于实例于构造函数之间。
虽然在所有实现中都无法访问到[[Prototype]],但可以通过isPropertypeOf()方法来确定对象之间是否存在这种关系。从本质上来讲,如果[[Propertype]]指向调用isPropertypeOf()方法的构造函数的原型对象,那么这个方法就返回true。
ECMAScript5增加了一个方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值,即返回相关的构造函数的原型对象。
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性于实例原型中的一个属性同名,那我们就在该实例中创建该属性,该属性将会屏蔽原型中的那个属性。
当对象实例添加一个属性的时候,这个属性会屏蔽原型对象中的保存的同名属性,不过使用delete操作符可以完全删除实例属性,从而让我们能够重新访问原型中的属性。
使用hasOwnProperty()方法可以检测一个属性是否存在于实例中,还是存在原型中,只在给定的属性存在于对象实例中时,才会返回true。
2.原型与in操作符
有两种方式使用in操作符:单独使用和在for-in循环中使用。在单独使用的时候,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在与实例中还是原型中。
在使用for-in循环时,返回的是所有能够通过对象访问的,可枚举的属性,其中既包括存在于实例中的属性,也包括存在与原型中的原型。屏蔽了原型中不可枚举属性的实例属性也会在for-in循环中返回。
要取得对象上所有可枚举的实例属性,可以使用ECMAScript5的Object.keys()方法。这个方法接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
3.更简洁的原型语法
之前的原型模式每次创建一个自定义类型的对象的时候,我们都要敲一遍原型,现在我们作出如下修改
function Person() {
}
Person.prototype = {
name: 'ydb',
sayName: function () {
console.log(this.name);
}
}
这样我们把一个对象字面量赋值给该构造函数的原型,重写了构造函数的默认原型。但是现在construcor不是指向了该构造函数,现在指向了Object构造函数,而且现在constructor变成了可以枚举的属性(构造函数的默认原型对象是不可枚举的)。
现在我们作出如下修改:
function Person() {
}
Person.prototype = {
name: 'ydb',
sayName: function () {
console.log(this.name);
}
}
Object.defineProperty(Person.prototype, 'constructor', {
enumerable: false,
value: Person
})
现在该构造函数的constructor属性变成不可枚举的,而且指向了该构造函数。
4.原型的动态性
先看如下代码:
function Person() {
}
var person1 = new Person();
Person.prototype.doSomethings = function () {
console.log('ydb');
}
person1.doSomethings();
我们在创建对象实例之后,给对象的原型上添加了方法,则该对象实例是可以使用该函数的,其原因是实例与原型之间的松散关系。当我们使用dosomethings函数的时候,首先会在对象实例上查找,没有找到会继续搜索原型。
5.原生对象的原型
所有原生引用类型(Obkect,Array,Strinhg等等),都在其构造函数的原型上定义了方法。所以我们可以在这些原生引用类型的构造函数的原型上自定义方法。
尽管可以这样子做,但是会导致以下问题:
1.某个实现中缺少某个方法,就在原生对象的原型中添加这个方法,那么当在另一个支持该方法的实现中就可能导致命名冲突
2.而且这样子做,也有可能意外地重写原生方法。
6.原型对象的问题
原型对象的问题如下:
1.它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。
2.由构造函数的原型的共享性质导致的问题。
对于那些只包含基本值的属性,在对象实例化之后,在实例的上添加一个同名属性,就可以屏蔽原型中的对应属性,但是那些包含引用类型的属性,其中的属性是一个指向一种引用类型的指针,当对象实例化之后,实例上的属性也就包含了指向那种引用类型的指针,那么该指针对应的引用类型数据一旦发生改变,所有实例之间的该属性指向的引用类型数据都发生了改变,这样子就会导致所多问题。可是,实例一般要有属于自己的全部属性的。这个问题也是导致很少有人单独使用原型模式创建对象实例的原因。
4.组合使用构造函数模式和原型模式
创建自定义类型的常见方法,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function () {
console.log(this.name);
}
var peroson1 = new Person('1');
var peroson2 = new Person('2');
结果,每一个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。可以说,这是用来定义引用类型的一种默认模式。
注意:上述几种依赖原型创建对象实例的模式,如果在已经创建了实例的情况下重写原型,那么就会切断现有实例于新原型之间的联系。因为实例创建之后,其内部的[[Prototype]]指向的是之前的原型对象,这个时候修改之后内部的[[Prototype]]是无法与新原型之间创建连接关系的,所以对象实例可能在调用某些方法或者使用某些属性的时候,导致找不到对应的方法或者属性而报错。
从头认识js-js中的对象的更多相关文章
- JS事件中的对象
在触发任何事件时都会产生一个对象.如:DOM事件,window事件等,都会产生一个对象,该对象就是当前元素的事件对象. DOM事件对象 <body> <input type=&quo ...
- 小姐姐手把手教你JS数组中的对象去重
有时候数据库中的数据重复的,我们另一个需求需要数据的唯一性 那么这时候就用到这个方法了 我还是以截图的方式发粗来 不然太丑了 见谅 console.log(map)打印出来的结果已经帮我们把需要的 ...
- JS——数组中push对象,覆盖问题,每次都创建一个新的对象
今天写运动框架时,发现将对象push进数组,后面的值会覆盖前面的值,最后输出的都是最后一次的值.其实这一切都是引用数据类型惹的祸. 如果你也有类似问题,可以继续看下去哦. 下面 ...
- js数组中去重对象
var allCourses = new Array();var coursesId = new Array();function findCourses() { Courses.data().eac ...
- js中判断对象具体类型
大家可能知道js中判断对象类型可以用typeof来判断.看下面的情况 <script> alert(typeof 1);//number alert(typeof "2" ...
- 浅解析js中的对象
浅解析js中的对象 原文网址:http://www.cnblogs.com/foodoir/p/5971686.html,转载请注明出处. 前面的话: 说到对象,我首先想到的是每到过年过节见长辈的时候 ...
- js中XMLHttpRequest对象实现GET、POST异步传输
js中XMLHttpRequest对象实现GET.POST异步传输 /* * 统一XHR接口 */ function createXHR() { // IE7+,Firefox, Opera, Chr ...
- JavaScript学习12 JS中定义对象的几种方式
JavaScript学习12 JS中定义对象的几种方式 JavaScript中没有类的概念,只有对象. 在JavaScript中定义对象可以采用以下几种方式: 1.基于已有对象扩充其属性和方法 2.工 ...
- js中推断对象详细类型
大家可能知道js中推断对象类型能够用typeof来推断. 看以下的情况 <script> alert(typeof 1);//number alert(typeof "2&quo ...
- JavaScript学习12 JS中定义对象的几种方式【转】
avaScript学习12 JS中定义对象的几种方式 转自: http://www.cnblogs.com/mengdd/p/3697255.html JavaScript中没有类的概念,只有对象. ...
随机推荐
- Spring Boot使用Liquibase最佳实践
Liquibase问题 随着项目的发展,一个项目中的代码量会非常庞大,同时数据库表也会错综复杂.如果一个项目使用了Liquibase对数据库结构进行管理,越来越多的问题会浮现出来. ChangeSet ...
- 24)PHP,数据库的基本知识
(1)数据库操作的基本流程: • 建立连接(认证身份) • 客户端向服务器端发送sql命令 • 服务器端执行命令,并返回执行的结果 • 客户端接收结果(并显示) • 断开连接 (2)php中操作数据库 ...
- 36)PHP,获取数据库数据并在html中显示(晋级3)
首先展示我的html代码和php文件的位置关系: 然后我的php文件: <?php class db { public $host ;//= "localhost";//定义 ...
- 吴裕雄--天生自然python机器学习:机器学习简介
除却一些无关紧要的情况,人们很难直接从原始数据本身获得所需信息.例如 ,对于垃圾邮 件的检测,侦测一个单词是否存在并没有太大的作用,然而当某几个特定单词同时出现时,再辅 以考察邮件长度及其他因素,人们 ...
- 隐马尔可夫随机场HMM
概率知识点: 0=<P(A)<=1 P(True)=1;P(False)=0 P(A)+P(B)-P(A and B) = P(A or B) P(A|B)=P(A,B)/P(B) =&g ...
- Nesterov方法的python实现
牛顿动量法,相比于上一篇Momentum,不一样的地方是应用了临时更新 这里用python对其进行简单实现,如下: # coding=utf-8 """ 基于小批量梯度下 ...
- 读写分离(AMOEBA)
博主本人平和谦逊,热爱学习,读者阅读过程中发现错误的地方,请帮忙指出,感激不尽 1. 环境准备: 1.1新增一台虚拟机 amoeba:192.168.247.80 架构(使用图片源自其它博文): 1. ...
- Qt QByteArray 与 char* 的转换
QByteArray 转换为 char * char *ch;//不要定义成ch[n]; QByteArray byte; ch = byte.data(); char * 转换为 QByteArra ...
- 博客已搬迁到 blog.vivym.xyz
博客已搬迁到 blog.vivym.xyz
- 多线程并发 了解 Thread
通过上一篇 多线程并发 (一) 了解 Java 虚拟机 - JVM 了解了java 虚拟机的构成以及对象的创建等.从Java虚拟机栈我们知道每当我们创建一个线程JVM就会给我们的线程分配一个私有的内存 ...