目录

一、什么是对象?

二、对象的创建(3种方法)

三、给对象添加修改数据。

JS遍历对象

概述

剥开Object的“伪装”

常用遍历方法

for..in..遍历

Object.keys遍历

Object.getOwnPropertyNames遍历

说好的for..of..,为什么无效


一、什么是对象?

对象是JavaScript的一个基本数据类型,是一种复合值,它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值。即属性的无序集合。

二、对象的创建(3种方法)

1、对象直接量/字面量:

var obj = {name: 'zsn',age: 18}console.log(obj.name);//zsn

2、构造函数:

系统自带的, ps: new Object(), Array(), Number(), Boolean(), Date()...

var obj = new Object();obj.name = 'zsn';console.log(obj.name);
//            zsn

2.自定义的: 为了和普通函数区分,首字母大写,采用大驼峰写法;

function Obj(name){this.name = name;this.age = 18
}
var obj = new Obj('zsn');
console.log(obj.name);//zsn
console.log(obj.age);//18

自定义构造函数的基本构造原理:

关键是有没有new这个操作符,不用new,Obj('zsn')就是一个函数的正常执行,没有返回值,则默认返回undefined;

而是用new操作符后js引擎就会将该函数看作构造函数看待,返回值就是一个对象了。demo如下:

function Obj(){this.age = 18;}//不用newconsole.log(Obj());//undefined//用newconsole.log(new Obj());//Obj {age: 18}

用new和不用new不同的原因:

不用new,函数内的this指向的是window,所以this.xxx定义的变量都是window上的属性,但为什么使用new后其中的this就不是window对象了呢?那是因为用new后,js引擎会在函数被进行两步隐士操作(假设构造函数名为Person):第一步, var this = Object.create(Peson.prototype);   (也是创建对象的一种方法,下边会讲到)  隐士的改变函数内this的含义,现在函数内的this是一个原型为Person.prototype, 构造函数为Person的对象(其实此过程就将想要的对象基本创造成功了,只是差些属性而已,从此可是看出构造函数创建对象的最根本原理是借用Object.create()方法来实现的,只不过被封装功能化了); 第二步, 在创建的对象设置完所需要的属性后,隐士的将创建的对象this通过return返回  return this;

通过代码的展现:

//  构造函数的原型Person.prototype = {say: function(){console.log('I am saying');}}//  构造函数function Person(){//  隐士操作//var this = Object.create(Person.prototype);//返回对象属性的设置this.name = "zsn";this.age = 18//  隐士操作//return this;}var person1 = new Person();console.log(person1.name); //zsnperson1.say(); //I am saying

上述两步理论的验证:

第一步:现在函数内的this是一个原型为Person.prototype, 构造函数为Person的对象

//  构造函数的原型Person.prototype = {say: function (){console.log('I am saying');}}//  构造函数function Person(){this.name ='zsn';this.age = 18;// 打印this对象的原型console.log(this.__proto__);// 验证this是否是Person构造函数的实例console.log(this instanceof Person);//true}new Person();//打印结果如下//  Object say: ()__proto__: Object// truePerson();//打印结果如下//  Window// false

第二步:隐士的将创建的对象this通过return返回

//  构造函数的原型

 Person.prototype = {say: function (){console.log('I am saying');}}//  构造函数function Person(){var that = Object.create(Person.prototype);that.name ='zsn';that.age = 18;return that;//提前返回that导致return this无法执行而失效}
var person = new Person();//此处不用new也是可以成功返回一个满足条件的对象,因为显示的返回了thatconsole.log(person.name); //zsnperson.say();//I am saying

ps. 关于显示返回that的问题,当我们用new生成对象,若我们显示return的是一个对象 / 引用值,则会导致return this失效,若返回的是原始值,则return this不会失效

3、Object.create(原型); 创建一个继承该原型的实例对象

关于此方法的一些事项:

(1)、若传参为Object.prototype,则创建的原型为Object.prototype,和 new Object()创建的对象是一样的

Object.create(Object.prototype) <==> new Object();
        (2)、若传参为空 或者 null,则创建的对象是没有原型的, 导致该对象是无法用document.write()打印会报错,因为document.write()打印的原理是调用Object.prototype.toString()方法,该对象没有原型,也就没有该方法,所以document.write()无法打印

由此延伸的知识点: 引用值都也是算作是对象,所以都可以用document.write()打印;原始值numebr, boolean, string都有自己对象的包装类,借助此机制也是可以用document.write()打印出的;但undefined 和 null既不是引用值,也没有对应的包装类,所以应该无法打印的,但大家会发现这两个值也是可是用document.write()打印的,因为这两个值被设定为特殊值,document.write()打印其是不用调用任何方法的,而是之直接打印其值。

三、给对象添加修改数据。

三、对象的增、删、改、查

1、增:

var obj = {};console.log(obj.name); //undefined (不会报错)obj.name = 'zsn';console.log(obj.name); //zsn

2、删:

var obj = {name : "zsn"};console.log(obj.name); //zsn delete obj.name;console.log(obj.name); //undefined

3、改:

var obj = {name : "zsn"};console.log(obj.name); //zsn obj.name = 'obj';console.log(obj.name); //obj

4、查:

var obj = {name : "zsn"};// 第一种方法console.log(obj['name']);//zsn //  第二种方法console.log(obj.name); //zsn

//p.s.最本质的是第一种方法,因为在使用第二种方法时,后台自动将其转换为第一种字符串的形式来查询 
ps.以上的增、删、改三种操作都只是针对当前对象的属性进行操作,而不会影响到当前对象的原型的属性。而查询是先看看当前对象本身是否设置了该属性,如果当前对象未设置该属性,则再看该对象的原型中是否设置了该属性,若两者都没有,则返回undefined

四、包装类:

1、五个原始值:number, string , boolean, undefined, null 其中number, string, boolean是分别拥有自己的包装类,而undefined和null是没有自己的包装类的

2.原始值不是对象,无法拥有自己的属性,但因为的包装类的存在,原始值就好似可以拥有自己的属性了,但其拥有的属性又有点特殊之处,如下用string来举例:

先看一段code

//  str是string类型的,非对象,不能拥有属性,为什么能打印出str.length?

 var str = 'abcd';console.log(str.length);//4

上边code中问题的解释:

// 因为每次执行完一条完整js语句后该类型对象的包装类就会将该语句包装,所以也就不会导致报错了,这些都是后台自己写的

var str = 'abcd';// var str1 = new String('abcd');console.log(str.length);//4//var str1 = new String('abcd');//console.log(str1.length);

注意:每次包装类包装完一次完整语句后就会被销毁。(即解释一条语句,用包装类包装一次,然后销毁),这回导致其和正常对象的一些不同之处,如下例子

var str = 'abcd';// var str1 = new String('abcd');//销毁str.len = 4;//var str1 = new String('abcd');//str1.len = 4;//销毁console.log(str.len); //undefinedvar str1 = new String('abcd');console.log(str.len); //str1为刚创建的对象,其len属性自然为undefiend//销毁

一个易错的坑:

// 总之记得'原始值包装类''销毁'这两句话就行

 var str = 'abcd';str.length = 2;console.log(str);console.log(str.length);

JS遍历对象

概述

在Javascript编程时,经常需要遍历对象的键、值,ES5提供了for...in用来遍历对象,然而其涉及对象属性的“可枚举属性”、原型链属性等,总会让人多少摸不着头脑。
本文将由Object对象本质探寻各种遍历对象的方法,并区分常用方法的特点。

本文所提的对象,特指Object的实例,不包含Set、Map、Array等数据集对象。

剥开Object的“伪装”

Javascript的对象,每一个属性都有其“属性描述符”,主要有两种形式:数据描述符存取描述符

可以通过 Object.getOwnPropertyDescriptorObject.getOwnPropertyDescriptors两个方法获取对象的属性描述符。
以下通过示例说明:

var obj = {name: '10',_age: 25,get age(){return this._age;},set age(age){if(age<1){throw new Error('Age must be more than 0');}else{this._age = age;}}
};var des = Object.getOwnPropertyDescriptors(obj);
console.log(des);
/*** des: {*  name: {*    configurable: true,*    enumerable: true,*    value: "10",*    writable: true,*    __proto__: Object*  },*  _age: {*    configurable: true,*    enumerable: true,*    value: 25,*    writable: true,*    __proto__: Object*  },*  age: {*    configurable: true,*    enumerable: true,*    get: f age(),*    set: f age(age),*    __proto__: Object*  },*  __proto__: Object* }
*/

可以看到,

  • name、_age拥有 'configurable''enumerable''value''writable'四个属性描述符,统称数据描述符
  • age拥有'configurable''enumerable''get''set'四个属性描述符,统称存取描述符
  configurable enumerable value writable get set
数据描述符 Yes Yes Yes Yes No No
存取描述符 Yes Yes No No Yes Yes

对象的属性描述符,可以通过Object.definePropertyObject.defineProperties来修改(configurabletrue的条件下)
详细内容可以参考:MDN手册 Object.defineProperty

了解了这个之后,与今天主题相关的,也就是 'enumerable'这个属性描述符啦,其值为 true 时,我们称其为“可枚举的”,属性是否可枚举影响了我们在使用原生方法遍历对象时的结果,在本文后面,将详细说明。

掌握属性描述符,无论是对自己以后的代码编写,还是学习开源框架源码,都是十分基础而且重要的,在此处仅作介绍,并着重关注与本文主题相关的属性。

常用遍历方法

for..in..遍历

遍历自身及原型链上所有可枚举的属性

示例代码:

var Person = function({name='none', age=18, height=170}={}){this.name = name;this.age = age;this.height = height;
}Person.prototype = {type: 'Animal'
}var qiu = new Person()// 将height属性设置为 不可枚举
Object.defineProperty(qiu, 'height', {enumerable: false
})for(let n in qiu){console.log(n);
}// output: name age type

如以上代码所示,使用for..in..遍历,会将对象自身及其原型链上的所有可枚举属性全部遍历出来。
而往往我们并不需要将原型链上的属性也遍历出来,因此常常需要如下处理:

for(let n in qiu){// 判断是否实例自身拥有的属性if(qiu.hasOwnProperty(n)){console.log(n)}
}

因为for..in..在执行的时候,还进行了原型链查找,当只需要遍历对象自身的时候,性能上会收到一定影响。

Object.keys遍历

返回一个数组,包括对象自身的(不含继承的)所有可枚举属性

示例代码:

var Person = function({name='none', age=18, height=170}={}){this.name = name;this.age = age;this.height = height;
}Person.prototype = {type: 'Animal'
}var qiu = new Person()// 将height属性设置为 不可枚举
Object.defineProperty(qiu, 'height', {enumerable: false
})var keys = Object.keys(qiu);
console.log(keys)
// output: ['name', 'age']

通过上述代码,我们可以看到,Object.keys仅遍历对象本身,并将所有可枚举的属性组合成一个数组返回。
在很多情况下,其实我们需要的,也就是这样一个功能。
例如以下,将键值类型的查询param转换成url的query,不仅代码量少、逻辑清晰,而且可以通过链式的写法使得整体更加优雅。

const searchObj = {title: 'javascript',author: 'Nicolas',publishing: "O'RELLY",language: 'cn'
}
let searchStr = Object.keys(searchObj).map(item => `${item}=${searchObj[item]}`).join('&');
let url = `localhost:8080/api/test?${searchStr}`

遍历键值对的数据时,使用Object.keys真是不二之选。

Object.getOwnPropertyNames遍历

返回一个数组,包含对象自身(不含继承)的所有属性名

示例代码:

var Person = function({name='none', age=18, height=170}={}){this.name = name;this.age = age;this.height = height;
}Person.prototype = {type: 'Animal'
}var qiu = new Person()// 将height属性设置为 不可枚举
Object.defineProperty(qiu, 'height', {enumerable: false
})var keys = Object.getOwnPropertyNames(qiu);
console.log(keys)
// output: ['name', 'age', 'height']

与Object.keys的区别在于Object.getOwnPropertyNames会把不可枚举的属性也返回。除此之外,与Object.keys的表现一致。

说好的for..of..,为什么无效

在ES6中新增了迭代器与for..of..的循环语法,在数组遍历、Set、Map的遍历上,十分方便。然而当我应用在对象(特指Object的实例 )上时(如下代码),浏览器给我抛了一个异常:Uncaught TypeError: searchObj is not iterable

const searchObj = {title: 'javascript',author: 'Nicolas',publishing: "O'RELLY",language: 'cn'
}for(let n of searchObj){console.log(n)
}
// Uncaught TypeError: searchObj is not iterable

没错...这是一个错误的演示,在ES6中,对象默认下并不是可迭代对象,表现为其没有[Symbol.iterator]属性,可以通过以下代码对比:

const searchObj = {title: 'javascript',author: 'Nicolas'
};
const bookList = ['javascript', 'java', 'c++'];
const nameSet = new Set(['Peter', 'Anna', 'Sue']);console.log(searchObj[Symbol.iterator]); // undefined
console.log(bookList[Symbol.iterator]); // function values(){[native code]}
console.log(nameSet[Symbol.iterator]); // function values(){[native code]}// 注,Set、Map、Array的[Symbol.iterator]都是其原型对象上的方法,而非实例上的,这点需要注意

而for..of..循环,实际上是依次将迭代器(或任何可迭代的对象,如生成器函数)的值赋予指定变量并进行循环的语法,当对象没有默认迭代器的时候,当然不可以进行循环,而通过给对象增加一个默认迭代器,即[Symbol.iterator]属性,就可以实现,如下代码:

Object.prototype[Symbol.iterator] = function *keys(){for(let n of Object.keys(this)){ // 此处使用Object.keys获取可枚举的所有属性yield n}
}const searchObj = {title: 'javascript',author: 'Nicolas',publishing: "O'RELLY",language: 'cn',
};for(let key of searchObj){console.log(key)
}
// output: title author publishing language

以上代码确实获得了对象的所有键名,在生成器函数内,我们使用的是Object.keys获得所有可枚举的属性值,然而这并不是所有人都期望的,也许小明期望不可枚举的属性值也被遍历,而小新可能连[Symbol.iterator]也希望遍历出来,于是,这里产生了一些分歧,如何遍历有以下几种因素:
总结起来,对象的property至少有三个方面的因素:

  1. 属性是否可枚举,即其 enumerable属性描述符 的值;
  2. 属性的类型,是字符串类型、还是Symbol类型;
  3. 属性所属,包含原型,还是仅仅包含实例本身;

JavaScript 对象超细致总结相关推荐

  1. 如何获取HTML元素对应JavaScript对象?

    <!DOCTYPE html> <html><head><meta charset="UTF-8"><title>< ...

  2. JavaScript对象,方括号和算法

    by Dmitri Grabov 德米特里·格拉波夫(Dmitri Grabov) JavaScript对象,方括号和算法 (JavaScript Objects, Square Brackets a ...

  3. 我对javascript对象的理解

    前言 JavaScript这门语言除了基本类型都是对象,可以说JavaScript核心就是对象,因此理解JavaScript对象及其种种特性至关重要,这是内功.本文介绍了我对es5对象,原型, 原型链 ...

  4. 《JavaScript启示录》——1.21 JavaScript对象和Object()对象

    本节书摘来自异步社区<JavaScript启示录>一书中的第1章,第1.21节,作者:[美]Cody Lindley著,更多章节内容可以访问云栖社区"异步社区"公众号查 ...

  5. 《JavaScript启示录》——第1章 JavaScript对象 1.1创建对象

    本节书摘来自异步社区<JavaScript启示录>一书中的第1章,第1.1节,作者:[美]Cody Lindley著,更多章节内容可以访问云栖社区"异步社区"公众号查看 ...

  6. 如何通过其值获取JavaScript对象中的键?

    本文翻译自:How to get a key in a JavaScript object by its value? I have a quite simple JavaScript object, ...

  7. 如何从JavaScript对象中删除项目[重复]

    本文翻译自:How to remove item from a JavaScript object [duplicate] Possible Duplicate: 可能重复: How to remov ...

  8. 如何遍历JavaScript对象?

    本文翻译自:How to iterate over a JavaScript object? I have an object in JavaScript: 我在JavaScript中有一个对象: { ...

  9. 打印JavaScript对象的内容? [重复]

    本文翻译自:Print content of JavaScript object? [duplicate] This question already has an answer here: 这个问题 ...

最新文章

  1. JNI/NDK开发指南(八)——调用构造方法和父类实例方法
  2. 获取其他进程中ListView的文本
  3. 统计之都统计分析和R语言方面的图书
  4. ABAP中创建动态内表的三种方法(转载)
  5. PPT到底是天使还是魔鬼?
  6. 装mysql最后一步没响应_解决MySQL安装到最后一步未响应的三种方法
  7. ASP.NET企业开发框架IsLine FrameWork系列之三--七种武器
  8. 参考 JavaEE注解学习:(二)
  9. python网络爬虫学习笔记(十):数据存储
  10. 【CLR Via C#笔记】 值类型与拆装箱、参数传递
  11. Ubuntu 10.04的安装
  12. (五)Thymeleaf标准表达式之——[7-8]条件表达式 默认表达式
  13. 思维导图之《机器视觉知识体系》
  14. 思维导图学习---数据库相关基础思维导图(2)
  15. oracle IMP命令导入导出DMP文件
  16. android 安全管家权限管理,安卓应用滥用权限腾讯手机管家可手动关闭
  17. 如何生成CGCS2000坐标系等高线
  18. H5 video 自动播放(autoplay)不生效解决方案
  19. 基于 jquery ui 扩展Widget
  20. App性能测试揭秘(Android篇)

热门文章

  1. 微信小程序-酒店、餐厅订座系统源代码,酒店、餐厅预约系统源代码,含详细安装使用手册
  2. Linux分配全部可用磁盘空间流程
  3. Linux: Swap与swappiness
  4. 【毕业设计】Java ssm水果销售商城平台
  5. 【微信小程序系列】微信小程序简单的实现发送订阅信息
  6. ESP32-网络开发实例-通过NTP获取 Epoch/Unix时间
  7. python解题软件哪个好用_一些好用的Python工具整理(持续更新中)
  8. 论文阅读——ACNet:Strengthening the Kernel Skeletons for Powerful CNN via Asymmetric Convolution Blocks
  9. 数据库中的表导出成excel表格 并发送给客户端
  10. 【单端S参数与差分S参数转化】