第三章 对象

1、语法

两种形式定义:声明(文字)形式和构造形式

(1)文字语法大概是这样

1 var myObj = {
2 key: value
3 // ...
4 };

(2)构造形式大概是这样

1 var myObj = new Object();
2 myObj.key = value;

两者其实都是一样的,唯一区别就是,在文字声明中可以添加多个键值对,但是在构造形式中必须逐个添加

2、类型

javascript总共有六种主要类型(语言类型):string、 number、boolean、null、undefined、object

(1)内置对象(javascript里面一些对象子类型)

String、Number、Boolean、Object、Function、Array、Date、RegExp、Error

实际上他们是内置函数,内置函数可以当作构造函数来使用,从而可以构造一个对应子类型的新对象

举个栗子,第一个的原始值"I am a string"并不是一个对象,只是一个字面量并且是一个不可变的值。如果要在这个字面量上执行一些操作,比如获取长度、访问其中某个字符窜等,都需要将其转换为String对象。

1 var strPrimitive = "I am a string";
2 typeof strPrimitive; // "string"
3 strPrimitive instanceof String; // false
4 var strObject = new String( "I am a string" );
5 typeof strObject; // "object"
6 strObject instanceof String; // true
7 // 检查 sub-type 对象
8 Object.prototype.toString.call( strObject ); // [object String]

但是在必要时语言会自动把字符串字面量转换成一个 String 对象,也就是说你并不需要显式创建一个对象。引擎自动把字面量转换为String对象。

var strPrimitive = "I am a string";
console.log( strPrimitive.length ); // 13
console.log( strPrimitive.charAt( 3 ) ); // "m"

3、内容

存储在对象容器内部的是这些属性的名称,它们就像指针(从技术角度来说就是引用)一样,指向这些值真正的存储位置。在对象中,属性名永远都是字符串。

属性和方法

即使你在对象的文字形式中声明一个函数表达式,这个函数也不会“属于”这个对象——它们只是对于相同函数对象的多个引用

1 var myObject = {
2 foo: function() {
3 console.log( "foo" );
4 }
5 };
6 var someFoo = myObject.foo;
7 someFoo; // function foo(){..}
8 myObject.foo; // function foo(){..}

4、数组

数组也是对象,所以虽然每个下表都是整数,但是仍然可以给数组添加属性(但是不推荐),

最好只用对象来存储键 / 值对,只用数组来存储数值下标 / 值对

var myArray = [ "foo", 42, "bar" ];
myArray.baz = "baz";
myArray.length; // 3
myArray.baz; // "baz"

5、复制对象(浅复制和深复制)

(1)浅复制:ES6定义了Object.assign(...)方法来实现浅复制。Object.assign(...)方法的第一个参数是目标对象。它会遍历一个或多个源对象的所有可枚举的自有键并把它们复制(使用=操作符赋值)到目标对象,最后返回目标对象。

【由于 Object.assign(..) 就是使用 = 操作符来赋值,所以源对象属性的一些特性(比如 writable )不会被复制到目标对象】举个栗子:

function anotherFunction() { /*..*/ }
var anotherObject = {
c: true
};
var anotherArray = [];
var myObject = {
a: 2,
b: anotherObject, // 引用,不是复本!
c: anotherArray, // 另一个引用!
d: anotherFunction
};
anotherArray.push( anotherObject, myObject ); var newObj = Object.assign( {}, myObject );
newObj.a; // 2
newObj.b === anotherObject; // true
newObj.c === anotherArray; // true
newObj.d === anotherFunction; // true

6、属性描述

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

三个特性: writable (可写)、enumerable (可枚举)和 configurable (可配置)

在创建普通属性时属性描述符会使用默认值,我们也可以使用 Object.defineProperty(..)来添加一个新属性或者修改一个已有属性(如果它是 configurable )并对特性进行设置。

var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: true,
configurable: true,
enumerable: true
} );
myObject.a; // 2

7、Getter 和 Setter

在 ES5 中可以使用 getter 和 setter 部分改写默认操作,但是只能应用在单个属性上,无法应用在整个对象上。getter 是一个隐藏函数,会在获取属性值时调用。setter 也是一个隐藏函数,会在设置属性值时调用。

 1 var myObject = {
2 // 给 a 定义一个 getter
3 get a() {
4 return 2;
5 }
6 };
7 Object.defineProperty(
8 myObject, // 目标对象
9 "b", // 属性名
10 { // 描述符
11 // 给 b 设置一个 getter
12 get: function(){ return this.a * 2 },
13 // 确保 b 会出现在对象的属性列表中
14 enumerable: true
15 }
16 );
17 myObject.a; // 2
18 myObject.b; // 4

通常来说 getter 和 setter 是成对出现的(只定义一个的话通常会产生意料之外的行为)

 1 var myObject = {
2 // 给 a 定义一个 getter
3 get a() {
4   return this._a_;
5 },
6 // 给 a 定义一个 setter
7 set a(val) {
8   this._a_ = val * 2;
9 }
10 };
11 myObject.a = 2;
12 myObject.a; // 4

8、枚举

举个栗子:

 1 var myObject = { };
2 Object.defineProperty(
3 myObject,
4 "a",
5 // 让 a 像普通属性一样可以枚举
6 { enumerable: true, value: 2 }
7 );
8 Object.defineProperty(
9 myObject,
10 "b",
11 // 让 b 不可枚举
12 { enumerable: false, value: 3 }
13 );
14 myObject.b; // 3
15 ("b" in myObject); // true
16 myObject.hasOwnProperty( "b" ); // true
17 // .......
18 for (var k in myObject) {
19 console.log( k, myObject[k] );
20 }
21 // "a" 2

myObject.b 确实存在并且有访问值,但是却不会出现在 for..in 循环中(尽管可以通过 in 操作符来判断是否存在)。原因是“可枚举”就相当于“可以出现在对象属性的遍历中”

还有另外一个方法可以区分属性是否可枚举

var myObject = { };
Object.defineProperty(
myObject,
"a",
// 让 a 像普通属性一样可以枚举
{ enumerable: true, value: 2 }
);
Object.defineProperty(
myObject,
"b",
// 让 b 不可枚举
{ enumerable: false, value: 3 }
);
myObject.propertyIsEnumerable( "a" ); // true
myObject.propertyIsEnumerable( "b" ); // false
Object.keys( myObject ); // ["a"]
Object.getOwnPropertyNames( myObject ); // ["a", "b"]
  • propertyIsEnumerable(..) 会检查给定的属性名是否直接存在于对象中(而不是在原型链上)并且满足 enumerable:true 。
  • Object.keys(..) 会返回一个数组,包含所有可枚举属性, Object.getOwnPropertyNames(..)会返回一个数组,包含所有属性,无论它们是否可枚举。
  • in 和 hasOwnProperty(..) 的区别在于是否查找 [[Prototype]] 链,然而, Object.keys(..)和Object.getOwnPropertyNames(..) 都只会查找对象直接包含的属性。

9、遍历

ES5 中增加了一些数组的辅助迭代器,包括 forEach(..) 、 every(..) 和 some(..) 。每种辅助迭代器都可以接受一个回调函数并把它应用到数组的每个元素上,唯一的区别就是它们对于回调函数返回值的处理方式不同。

1、forEach(..) 会遍历数组中的所有值并忽略回调函数的返回值。

2、 every(..) 会一直运行直到回调函数返回 false (或者“假”值)

3、some(..) 会一直运行直到回调函数返回 true (或者“真”值)

但是上面的方法都只是遍历下标,怎样可以直接遍历值而不是数组的下标(或者对象属性)?噔噔噔噔...ES6增加了一种用来遍历数组的for..of循环语法【如果对此熬过本身定义了迭代器的话也可以遍历对象】:

1 var myArray = [ 1, 2, 3 ];
2 for (var v of myArray) {
3 console.log( v );
4 }
5 // 1
6 // 2
7 // 3

上面的例子,for..of循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法来遍历所有返回值。for..of只适用于数组,数组有内置的 @@iterator ,因此 for..of 可以直接应用在数组上。

1 ar myArray = [ 1, 2, 3 ];
2 var it = myArray[Symbol.iterator]();
3 it.next(); // { value:1, done:false }
4 it.next(); // { value:2, done:false }
5 it.next(); // { value:3, done:false }
6 it.next(); // { done:true }

但是,和数组不一样,普通的对象里面并没有内置对象@@iterator ,所以无法自动完成for..of遍历。但是可以给任何想遍历的对象定义 @@iterator

 1 var myObject = {
2 a: 2,
3 b: 3
4 };
5 Object.defineProperty( myObject, Symbol.iterator, {
6 enumerable: false,
7 writable: false,
8 configurable: true,
9 value: function() {
10 var o = this;
11 var idx = 0;
12 var ks = Object.keys( o );
13 return {
14 next: function() {
15 return {
16 value: o[ks[idx++]],
17 done: (idx > ks.length)
18 };
19 }
20 };
21 }
22 } );
23 // 手动遍历 myObject
24 var it = myObject[Symbol.iterator]();
25 it.next(); // { value:2, done:false }
26 it.next(); // { value:3, done:false }
27 it.next(); // { value:undefined, done:true }
28 // 用 for..of 遍历 myObject
29 for (var v of myObject) {
30 console.log( v );
31 }
32 // 2
33 // 3

你不知道的JavaScript(上)this和对象原型(二)的更多相关文章

  1. 读书笔记-你不知道的JavaScript(上)

    本文首发在我的个人博客:http://muyunyun.cn/ <你不知道的JavaScript>系列丛书给出了很多颠覆以往对JavaScript认知的点, 读完上卷,受益匪浅,于是对其精 ...

  2. 《你不知道的javascript(上)》笔记

    作用域是什么 编译原理 分词/词法分析 这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元 解析/语法分析 词法单元流(数组)转换成一个由元素逐级嵌套所组成 ...

  3. 你不知道的JS之 this 和对象原型(一)this 是什么

     原文:你不知道的js系列 JavaScript 的 this 机制并没有那么复杂 为什么会有 this? 在如何使用 this 之前,我们要搞清楚一个问题,为什么要使用 this. 下面的代码尝试去 ...

  4. 《你不知道的Javascript》感悟篇—对象属性遍历的那些事

    划重点 本篇笔者将重点介绍JavaScript中 getOwnPropertyNames .Object.keys.for ... in 的使用及他们之间的异同点. getOwnPropertyNam ...

  5. JavaScript -基础- 函数与对象(二)String

    一.判断数据类型typeof与判断对象类型instanceof 1.typeof typeof只能判断基础数据类型,无法判断引用数据类型 <script> var s="hell ...

  6. JavaScript的构造器与对象(二)

    constructor 的用法:对象的构造函数  每一个函数的Prototype属性指向的对象都包含唯一一个不可枚举属性constructor,该属性的值是这么一个对象:它指向了它所在的构造函数. 语 ...

  7. JavaScript对象原型

    一.MDN上的解释(有点抽象) 基于原型的语言? JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模 ...

  8. JavaScript 之 原型对象、对象原型 —— { }

    JavaScript -- 构造函数 // 构造函数 function Player(name, age) { this.name = name; this.age = age; } JavaScri ...

  9. JavaScript中的对象与原型—你不知道的JavaScript上卷读书笔记(四)

    一.对象 对象可以通过两种形式定义:声明(文字)形式和构造形式.即: var myObj = { key: value // ... }; 或: var myObj = new Object(); m ...

  10. 《你必须知道的javascript(上)》- 2.this与对象原型

    1 关于this 1.1 为什么使用this 随着你的使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用this则不会这样.当我们介绍对象和原型时,你就会明白函数可以自动引用合适的上下 ...

随机推荐

  1. opencv 4 图像处理 (1 线性滤波,非线性滤波)

    1 线性滤波:方框滤波.均值滤波.高斯滤波 1.1方框滤波(box Filter) 1.2均值滤波(blur函数) 缺陷: 1.3高斯滤波(GaussianBlur函数) 1.4线性滤波核心API函数 ...

  2. 借汇编之力窥探String背后的数据结构奥秘

    熟悉C++.java.VB等编程语言的朋友都知道String(字符串),它是编程语言中表示文本的数据类型,字符串由若干字符组成的,是所有编程语⾔中⾮常重要的成员.可能很多朋友平时只是使用它,没有仔细研 ...

  3. 【Luogu P3834】可持久化线段树(主席树)

    Luogu P3834 可持久化数据结构就是支持在历史版本上进行查询和修改操作的数据结构. 主席树就是对线段树的改进,使之可持久化. 前置知识:动态开点线段树 我们利用权值(值域)线段树统计区间内的数 ...

  4. linux服务器cpu信息查看详解

    在linux系统中,提供了/proc目录下文件,显示系统的软硬件信息.如果想了解系统中CPU的提供商和相关配置信息,则可以查/proc/cpuinfo.但是此文件输出项较多,不易理解.例如我们想获取, ...

  5. python的reduce,map,zip,filter和sorted函数

    一.    reduce(function,Iterable),它的形式和map()函数一样.不过参数function必须有两个参数. reduce()函数作用是:把结果继续和序列的下一个元素做累积计 ...

  6. https安全在哪里,原理是什么?

    转载请标明出处:http://blog.csdn.net/shensky711/article/details/52214842 本文出自: [HansChen的博客] Https通信基本过程 在通信 ...

  7. 【JavaEE】之MyBatis动态SQL

    动态SQL就是在SQL语句中添加一些标签,以完成某些逻辑.通常用到的动态SQL标签有<if>.<choose>.<where>.<trim>.<s ...

  8. PHP如何获取视频总时长与码率等信息

    利用PHP中的FFmpeg读取视频播放时长与码率等信息   function getVideoInfo($file) {    define('FFMPEG_PATH', '/usr/local/ff ...

  9. VS #region

    1.C# 预处理指令 #region使您得以在使用Visual Studio代码编辑器的大纲显示功能时指定可展开或折叠的代码块.    #region   name    其中:name      希 ...

  10. SpringBoot和SpringCloud的版本对应关系

    1.详细的SpringBoot和SpringCloud对应的关系: Spring官方对应关系 2.springCloud与各组件的版本对应关系 官方文档