一、对象

对象可以通过两种形式定义:声明(文字)形式和构造形式。即:

var myObj = {
key: value
// ...
}; 或: var myObj = new Object();
myObj.key = value;

对象还有一些内置对象: String、Number、Boolean、Object、Function、Array、Date、RegExp、Error。

访问对象属性可以使用. 操作符(属性访问)或者[] 操作符(键访问),区别在于:于. 操作符要求属性名满足标识符的命名规范,而[".."] 语法

可以接受任意UTF-8/Unicode 字符串作为属性名。

属性描述符

ES5 开始,JS所有的属性都具备了属性描述。

1、 Writable(是否可修改属性)
var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: false, // 不可写!
configurable: true,
enumerable: true
} );
myObject.a = 3; (严格模式会报错)
myObject.a; // 2
2、 Configurable

只要属性是可配置的,就可以使用defineProperty(..) 方法来修改属性描述符。但是有一个例外:即便属性是configurable:false, 我们还是可以把writable 的状态由true 改为false,但是无法由false 改为true。除了无法修改,configurable:false 还会禁止删除这个属性。

3、 Enumerable(是否可枚举)

表示能否通过for-in 循环返回属性

4、 访问描述符(Getter、Setter)

当你给一个属性定义getter、setter 或者两者都有时,这个属性会被定义为“访问描述符”(和“数据描述符”相对)。对于访问描述符来说,JavaScript 会忽略它们的value 和writable 特性,取而代之的是关心set 和get(还有configurable 和enumerable)特性。

遍历

for..in 循环可以用来遍历对象的可枚举属性列表(包括[[Prototype]] 链),实际上并不是在遍历值,而是遍历下标来指向值。

ES5 中增加了一些数组的辅助迭代器,包括forEach(..)、every(..) 和some(..)

ES6 增加了一种用来遍历数组for..of 循环语法(如果对象本身定义了迭代器的话也可以遍历对象),直接遍历值而不是数组下标。数组有内置的@@iterator,因此for..of 可以直接应用在数组上,普通的对象没有内置的@@iterator,所以无法自动完成for..of 遍历。

关于new操作符

调用构造函数实际上会经历以下4个步骤:

(1) 创建一个新对象;

(2) 让空对象的__proto__(IE没有该属性)成员指向了构造函数的prototype成员对象;

(3) 使用apply调用构造器函数,this绑定到空对象obj上;

(4) 返回新对象。

function NEW_OBJECT(Foo){

	var obj={};
obj.__proto__=Foo.prototype;
obj.__proto__.constructor=Foo;
Foo.apply(obj,arguments)
return obj; }

二、原型

1. [[Prototype]]

JavaScript 中的对象有一个特殊的[[Prototype]] 内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时[[Prototype]] 属性都会被赋予一个非空的值。所有普通的[[Prototype]] 链最终都会指向内置的Object.prototype。

当我们试图访问一个对象下的某个属性的时候,会在JS引擎触发一个GET的操作,首先会查找这个对象是否存在这个属性,如果没有找的话,则继续在prototype关联的对象上查找,以此类推。如果在后者上也没有找到的话,继续查找的prototype,这一系列的链接就被称为原型链。

2. property

只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性。

3. constructor

,对象的.constructor 会默认指向一个函数,这个函数可以通过对象的.prototype引用,.constructor 并不是一个不可变属性。它是不可枚举的,但是它的值是可写的(可以被修改)。

一种混合模式:

function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor : Person,
sayName : function(){
alert(this.name);
}
} var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true

4. 继承

function Foo(name) {
this.name = name;
} Foo.prototype.myName = function() {
return this.name;
}; function Bar(name,label) {
Foo.call( this, name );
this.label = label;
} // 我们创建了一个新的Bar.prototype 对象并关联到Foo.prototype
Bar.prototype = Object.create( Foo.prototype );
// 注意!现在没有Bar.prototype.constructor 了
// 如果你需要这个属性的话可能需要手动修复一下它
Bar.prototype.myLabel = function() {
return this.label;
};
var a = new Bar( "a", "obj a" );
a.myName(); // "a"
a.myLabel(); // "obj a"

上面代码的核心为语句Bar.prototype = Object.create( Foo.prototype )。调用Object.create(..) 会凭空创建一个“新”对象并把新对象内部的[[Prototype]] 关联到你指定的对象(本例中是Foo.prototype)。

备注:Object.create原理

上述代码如果用以下两种方式:

// 和你想要的机制不一样!
Bar.prototype = Foo.prototype; // 基本上满足你的需求,但是可能会产生一些副作用 :(
Bar.prototype = new Foo();

Bar.prototype = Foo.prototype 并不会创建一个关联Bar.prototype 的新对象,它只是让Bar.prototype 直接引用Foo.prototype 对象。因此当你执行类似Bar.prototype.

myLabel = ... 的赋值语句时会直接修改Foo.prototype 对象本身。

Bar.prototype = new Foo() 的确会创建一个关联到Bar.prototype 的新对象。但是它使用了Foo(..) 的“构造函数调用”,如果函数Foo 有一些副作用(比如写日志、修改状态、注册到其他对象、给this 添加数据属性,等等)的话,就会影响到Bar() 的“后代”,后果不堪设想。

Bar.prototype = new Foo() 的确会创建一个关联到Bar.prototype 的新对象。但是它使用了Foo(..) 的“构造函数调用”,如果函数Foo 有一些副作用(比如写日志、修改状态、注册到其他对象、给this 添加数据属性,等等)的话,就会影响到Bar() 的“后代”,后果不堪设想。

Object.create的polyfill

if (!Object.create) {
Object.create = function(o) {
function F(){}
F.prototype = o;
return new F();
};
}

ES6 添加了辅助函数Object.setPrototypeOf(..),可以用标准并且可靠的方法来修改关联。

我们来对比一下两种把Bar.prototype 关联到Foo.prototype 的方法:

// ES6 之前需要抛弃默认的Bar.prototype
Bar.ptototype = Object.create( Foo.prototype );
// ES6 开始可以直接修改现有的Bar.prototype
Object.setPrototypeOf( Bar.prototype, Foo.prototype );

JavaScript中的对象与原型—你不知道的JavaScript上卷读书笔记(四)的更多相关文章

  1. Javascript中的对象和原型(3)

    在Javascript中的对象和原型(二)中我们提到,用构造函数创建的对象里面,每个对象之间都是独立的,这样就会降低系统资源的利用率,解决这样问题,我们就要用到下面提到的原型对象. 一 原型对象 原型 ...

  2. Javascript中的对象和原型(三)(转载)

    在Javascript中的对象和原型(二)中我们提到,用构造函数创建的对象里面,每个对象之间都是独立的,这样就会降低系统资源的利用率,解决这样问题,我们就要用到下面提到的原型对象. 一 原型对象 原型 ...

  3. Javascript中的对象和原型(一)(转载)

    面向对象的语言(如Java)中有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但是,JavaScript 没有类的概念,因此它的对象也与基于类的语言中的对象有所不同. 要了解面向对象,首 ...

  4. javascript中的对象,原型,原型链和面向对象

    一.javascript中的属性.方法 1.首先,关于javascript中的函数/“方法”,说明两点: 1)如果访问的对象属性是一个函数,有些开发者容易认为该函数属于这个对象,因此把“属性访问”叫做 ...

  5. Javascript中的对象和原型

    一 原型对象 原型对象实际上就是构造函数的一个实例对象,和普通的实例对象没有本质上的区别.可以包含特定类型的所有实例的共享属性或者方法.这样,如果我们需要修改所有实例中的属性或者方法,就只需要修改一处 ...

  6. Javascript中的对象和原型(二)(转载)

    上一篇中提到了JavaScript中对象的创建的一些基本操作,接下来讨论下继续讨论. 一 工厂模式 我们知道,要创建一个对象我们可以用如下代码: var user = new Object(); // ...

  7. JavaScript 中的事件流和事件处理程序(读书笔记思维导图)

    JavaScript 程序采用了异步事件驱动编程模型.在这种程序设计风格下,当文档.浏览器.元素或与之相关的对象发生某些有趣的事情时,Web 浏览器就会产生事件(event). JavaScript ...

  8. JavaScript内置对象与原型继承

    (一)   理解JavaScript类定义 1>关于内置对象理解 console.log(Date.prototype.__proto__===Object.prototype    //tru ...

  9. JavaScript中判断对象类型方法大全1

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

随机推荐

  1. Codeforces Round 589 (Div. 2) 题解

    Is that a kind of fetishism? No, he is objectively a god. 见识了一把 Mcdic 究竟出题有多神. (虽然感觉还是吹过头了) 开了场 Virt ...

  2. strcasecmp()函数

    函数介绍: strcasecmp用忽略大小写比较字符串.,通过strcasecmp函数可以指定每个字符串用于比较的字符数,strncasecmp用来比较参数s1和s2字符串前n个字符,比较时会自动忽略 ...

  3. 深入理解Java8中Stream的实现原理

    Stream Pipelines 前面我们已经学会如何使用Stream API,用起来真的很爽,但简洁的方法下面似乎隐藏着无尽的秘密,如此强大的API是如何实现的呢?比如Pipeline是怎么执行的, ...

  4. Azure EA (2) 使用Postman访问国内Azure Billing API

    <Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的Azure China 请读者先看一下之前的文档内容:Azure EA (1) 查看国内Az ...

  5. Linux shell自动读mongo数据、远程获取文件大小示例脚本

    1.示例1 功能:对mongoDB导出数据,根据sid的不同状态进行统计 技术点:shell bash  读写文件.字符串截取.函数.用多个文件提到的map.grep查找并赋值给变量 #!/bin/b ...

  6. 记录一个终端入网小助手的bug

    背景:技术leader拿到一台超薄笔记本,系统标准化安装,笔记本一开机风扇嗡嗡响,键盘也开始发烫,资源占用排名前三的进程都是终端管理软件,一下子就找上门了.处理:进程分析发现异常,卸载入网小助手后恢复 ...

  7. 【转载】什么是NVMe?

    什么是NVMe? [转载]什么是NVMe:http://storage.it168.com/a2018/0921/5045/000005045252.shtml NVMe是Non-Volatile M ...

  8. 算法初步---基本的数据结构(java为例)

    最近搞算法,觉得超级吃力的,一直以为数学好的,数学可以考试满分,算法一定没什么问题,贱贱地,我发现我自己想多了,还是自己的基础薄弱吧,今天我来补补最基础的知识. 算法(Algorithm)是指解题方案 ...

  9. 开发技术--Python核心技术B

    开发|Python核心技术B B篇,主要介绍Python的自定义函数,匿名函数,面向对象,模块化. 由于不涉及基础的知识,我会将重难点加以解释. 前言 目前所有的文章思想格式都是:知识+情感. 知识: ...

  10. ubuntu 18.04 修改Apache默认目录

    ubuntu 18.04 修改Apache默认目录 安装是直接运行 sudu apt install apache2 安装之后要修改目录 vi /etc/apache2/sites-available ...