📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍
文章目录
- 写在前面的话
- 对象的概述
- 对象的三个属性
- 对象的方法
- 对象的创建
- 属性的操作
- Object 常用方法
- 总结陈词
写在前面的话
本篇博文再次回归JavaScript
系列,不知道是不是Java
写多了,偶尔换JavaScript
就和打鸡血一样兴奋。
这里先介绍一下 JS 对象,现今的后端开发人员,往往都在与 Vue、ElementUI 操作打交道,对JavaScript
的了解知之甚少,JS 也有对象吗?那不是 Java 的概念,JS 也有吗?
推荐文章:
《无所不能的JavaScript · prototype 原型链》
《无所不能的JavaScript:ES6入门》
《无所不能的JavaScript · 异步编程》
《程序猿学会 Vue · 基础与实战篇》
对象的概述
- 对象是 JavaScript 的基本数据类型,可以看做是字符串到值的映射(key-value)的无序集合;
- 除了字符串、数字、true、false、null和undefined外,JavaScript 中的值都是对象。
- 对象除了保持自有的属性,还可以从一个称为原型的对象继承属性,这种原型式继承,是JavaScript 的核心特征。
- 如果变量x是对象,那么var y = x,对y的修改会影响到x,这里x和y都是对象的引用(类似Java的传址)。
- 可以通过对象直接量、关键字new和Object.create()函数三种方式创建对象,下面会详细介绍。
对象的三个属性
每一个对象都有与之相关的原型(prototype)、类(class)和可扩展性(extensible)。
原型 prototype:
在ECMAScript5 中,使用 Object.getPrototype() 来获取原型。
检测原型可以使用 isPrototypeOf() 功能和 instanceof 运算符类似,前者是函数用法。
原型的若干规则说明:
规则1:除null外的每一个JS对象,都和原型相关联,对象都从原型继承属性;
规则2:Object的原型不继承任何属性(即Object.prototype没有原型),除此之外的所有对象都是普通对象,具有原型;
规则3:所有内置构造函数具有继承自Object.prototype的原型,如Date等,因此,new Date()创建的对象同时继承Object.prototype和Date.prototype的属性;
规则4:所有通过对象直接量创建的对象都具有同一个原型对象,可以使用Object.prototype获得对原型对象的引用;
var a = {a:1}; //直接量方式创建对象
Object.prototype.isPrototypeOf(a); //返回true
arr instanceof Object; //返回true
a.constructor.prototype.isPrototypeOf(a); //返回true
规则5:通过关键字new+构造函数调用创建的对象,它的原型是构造函数的prototype属性的值,例如,new Array()创建的对象的原型就是Array.prototype,当然了Object.prototype也是;
类 class :
对象的类属性是一个字符串,可以使用toString()获取,获得这样格式的字符串:[object class],再截取第八位到倒数第二位即可。
// 获取变量的类型
function classof(o) {
if(o === null) return "Null";
if(o === undefined) return "Undefined";
return Object.prototype.toString.call(o).slice(8,-1);
}
对象序列化
对象序列化是指将对象转换为字符串,也可将字符串还原为对象。
ECMAScript5 提供了JSON.stringify()和JSON.parse()用来序列化和反序列化。
ECMAScript3 引入 json2.js,就具备了类似功能,IE低版本可能需要引入。
对象的方法
所有对象都从 Object.prototype 继承属性,这些继承属性主要是方法。
常用的有 tostring(),toLocaleString(),toJSON(),valueOf(),isPrototypeOf ,hasOwnProperty() 和 propertyIsEnumerable()等等。
- isPrototypeOf():检测原型,用法Object.prototype.isPrototypeOf(a);
- hasOwnProperty():判断自有属性,用法o.hasOwnProperty(“x”);
- propertyIsEnumerable():判断可枚举的自有属性,用法o. propertyIsEnumerable(“x”);
- tostring():该方法没有参数,它将返回一个表示调用这个方法的对象的字符串。
- valueOf():JavaScript中valueOf函数方法是返回指定对象的原始值。
- toJSON():Object.prototype实际上没有定义此方法,但对于需要执行序列化的对象来说,JSON.stringify()方法会调用该方法。
valueOf 和 tostring 的区别
valueOf 偏向于运算,toString偏向于显示,自定义对象一般都重写他们。
1、 在进行对象转换时(例如:alert(a)),将优先调用toString方法,如若没有重写toString将调用valueOf方法,如果两方法都不没有重写,但按Object的toString输出。
2、 在进行强转字符串类型时将优先调用toString方法,强转为数字时优先调用valueOf。
3、 在有运算操作符的情况下,valueOf的优先级高于toString。
4、 valueOf如果返回本身的话(而不是原始值),一般会去使用toString。
对象的创建
【方式一:对象直接量】
示例1:
示例2:
var a = {
b:1, ---注意这里是逗号
toString:function(){ return 1;} //可以重写toString方法为自己的方法
}
alert(a); //JS的alert输出对象,对象调用toString方法,比如这里就是a.toString();
说明如下:
规则1:属性名可以是JS标识符也可以是字符串直接量(有无引号皆可),属性值任意;
规则2:属性名如果用到空格,连字符或者是保留字,则必须用字符串表示;
规则3:对象直接量是一个表达式,每次运算都会创建并初始化一个新的对象,这点注意了;
【方式二:new关键字+构造函数】
var o = new Object; //创建一个空对象
var a = new Array; //创建一个空数组,和[]一样
说明如下:
规则1:JS语言核心中的原始类型都包含内置构造函数;
规则2:除了这些内置构造函数外,也可以使用自定义构造函数来初始化,如例2;
function employee(name,job,born){
this.name=name;
this.job=job;
this. born = function(){return born};
}
var bill=new employee("Bill Gates","Engineer",1985); //这里var不能写成employee
构造函数可以使用prototype关键字为其赋予额外的属性和方法,在对象生成之前或者之后都有效。
var path = function(){
return {
anchor:0,
init:function(_anchor){
this.anchor=_anchor;
}
}
};
此种方式定义,更有利于模块化管理,封装多个属性和方法,可以构造出多个(写成function path() 也是一样的)。
使用:var a = path(); a.init();
这里的init方法不可替换回var init =function(){…},因为此处return的是一个object,object里面都是key-value键值对。
var init =function(){…},可以放在return外面,但是只能作为私有属性,在path函数里面使用。
【方式三:Object.create()】
create是一个静态函数,参数是一个对象的原型,这种方式比较少用。
var o1 =Object.create({x:1}); //o1继承了属性x
var o2 =Object.create(null); //o2不继承任何属性和方法,包括toString方法
var o3 = Object.create(Object.prototype); //效果和new Object以及{}一样
属性的操作
【属性访问表达式】
可以通过点(.)或者方括号([])运算符来进行属性访问,也称为属性访问表达式。
语法1:expression . identifier
语法2:expression [expression ]
语法解析:
运算符左侧是一个表达式,它返回一个对象/数组,第一种方式不适合数组;
对于点来说,右侧必须是一个以属性名称命名的简单标识符;
对于方括号来说,右侧必须是一个计算结果为字符串的表达式,这个字符串就是属性的名称(对于数组来说,右侧是要访问数组元素的索引)。
运作解析:
两种方式对比:
显然点的方式更加简单,但这种方式只适用于要访问的属性名称是合法的标识符,并且需要知道要访问的属性名称。
如果属性名称是一个不合法标识符(如数字,空格或保留字等等),则必须用方括号。
如果属性名称是通过运算得出的值而不是固定的值的时候,则必须用方括号。
【属性继承】
【属性访问错误】
规则1:查询一个不存在的属性不会报错,只会返回undefined;
规则2:如果对象不存在,比如null或者undefined,那么试图查询这个不存在的对象的属性就会报错;
规则3:有一些属性是只读的,不能重新赋值,比如prototype;
【删除属性】
delete一元运算符,是用来删除对象属性或者数组元素,它的操作数是一个属性访问表达式。
语法:delete user.name; //user不再具有name属性
规则1:delete运算符只是断开和宿主的联系,不会去操作属性中的属性;
规则2:delete只能删除自身属性,不能删除继承属性(必须到原型中进行删除,并且这会影响到所有继承对象);
规则3:当删除成功、删除不存在属性、不是属性表达式等情况时,返回true;返回fasle的情况较少,一般也不会用到返回值;
规则4:删除数组元素后,数组长度不变,空的地方用undefined填充;
【检测属性】
检测属性是否存在对象,有如下方法:in运算符、hasOwnProperty() 和 propertyIsEnumerable()
in运算符:左侧是字符串形式的属性名,右侧是对象,如果对象的自有属性或继承属性中包含这个属性则返回true;
hasOwnProperty()方法:用来检测是否是对象自有属性,对于继承属性返回false;
propertyIsEnumerable()方法:是前者的加强版,只有属性是自有而且是可枚举的才返回true;
操作示例:
【枚举属性 - 遍历】
使用for/in循环可以遍历对象中所有_可枚举 _的属性(_包括自有和继承的 _),并把属性名称赋值给循环变量。
其中,对象继承的内置方法是不可枚举的,程序中添加的属性都是可枚举的。
除了for/in循环外,JS还提供了两个用以枚举属性名称的函数,keys()和getOwnPropertyNames()。
判断a对象的属性个数: Object.getOwnPropertyNames(a).length
//例1 -- 内置方法不可枚举,不会输出:
var o = {x:1,y:2,z:3,funX:function(){alert(1)}};
Object.prototype.m = 4;
alert(o.propertyIsEnumerable("x")); // true
alert(o.propertyIsEnumerable("toString")); // false
for(p in o) console.log(p); // 输出x,y,z,funX,m
//例2 -- 跳过继承的属性和所有方法(接上例):
for(p in o){
if(!o.hasOwnProperty(p)) continue; // 跳过继承的属性
if(typeof o[p]=== 'function') continue; // 跳过方法
console.log(p); // 函数funX和继承的m跳过了
}
keys和getOwnPropertyNames的区别
说明:返回对象自己(非原型继承的属性)的属性名称,包括函数。
方法:
Object.keys(object); // 返回一个由给定对象的所有_可枚举 _自身属性的属性名组成的数组。
Object.getOwnPropertyNames(object); // 类似keys,但是包括不可枚举的属性,即返回更多的属性。
参数:object,如果不是object类型,则引发TypeError异常。
【属性的setter和getter】
ECMAScript5中的对象存取器属性:getter和setter,存取器属性IE6、7、8不支持。
getter 是一种获得属性值的方法,setter是一种设置属性值的方法。巧用get和set,能够直接操作对象属性实现读写,可以极大的提高编程效率。
如何定义?有2种办法:在对象初始化的时候定义,在对象定义后的时候定义,见下面例子。
//demo1 -- 在对象初始化的时候定义
var obj = {
val:100,
get getval(){
return this.val;
},
set setval(x){
this.val = x;
}
}
console.log(obj.getval);
obj.setval = 101;
console.log(obj.getval);
//demo2 -- 在对象定义后的时候定义
var obj2 = {
val:200
}
obj2.__defineGetter__('name',function(){return this.val});
obj2.__defineSetter__('name',function(name){this.val = name;})
console.log(obj2.name)
obj2.name = 201;
console.log(obj2.name);
规则1:setter和getter定义的属性称为“存取器属性”,它们不同于“数据属性”,“数据属性”只是一个简单的值;
规则2:set和get 方法不一定要同名,名字任意,也可以不仅仅简单返回一个属性的值,而是组合的复杂表达式;
规则3:set和get 方法也可以被继承;读取set属性的时候总返回undefined,set忽视返回值;
规则4:注意这里set和get方法的调用,并不像函数那样调用,调用get直接像属性那样,set是用赋值方式调用;
规则5:更多时候并不是简单的像例子那样,针对某个属性set和get,而是封装一些常用方法;
【属性的特性】
属性除了名字和值外,还有可写、可配置和可枚举的特性,本章节涉及的内容,IE6、7、8不支持。
一般认为数据属性具有值(value)、可写性(writable)、可枚举性(enumerable)和可配置性(configurable)这四个特性。
对于存取器属性来说,不具备值和可写性,增加了set和get特性。
通过调用getOwnPropertyDescriptor()可以获得某个对象的某个自有属性 的特性,要获取继承的属性,要通过getPrototypeOf()方法。
设置特性可以使用defineProperty()和defineProperties(),更详细的请参考书籍6.7。
//常用操作示例:
let o = {x:1,y:2,z:3,funX:function(){alert(1)}};
Object.prototype.m = 4;
Object.getOwnPropertyDescriptor(o,'x'); // 获得 Object {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(o),'m'); // 获得继承的属性m的特性
Object.defineProperty(o,"x",{value:2,writable:false}); //修改属性x的特性
Object.getOwnPropertyDescriptor(o,'x'); // 再次获取 Object {value: 2, writable: false, enumerable: true, configurable: true}
Object 常用方法
Object.create(prototype[,descriptors])
这个方法用于创建一个对象,并把其prototype属性赋值为第一个参数,同时可以设置多个descriptors,关于decriptor下一个方法就会介绍这里先不说。只需要这样就可以创建一个原型链干净对象了。
const o = Object.create({
"say": function () {
alert(this.name);
},
"name":"Byron"
});
Object.defineProperty(O,Prop,descriptor) / Object.defineProperties(O,descriptors)
想明白这两个函数必须明白descriptor是什么,在之前的JavaScript中对象字段是对象属性,是一个键值对,而在ECMAScript5中引入property,property有几个特征:
- value:值,默认是undefined
- writable:是否是只读property,默认是false,有点像C#中的const
- enumerable:是否可以被枚举(for in),默认false
- configurable:是否可以被删除,默认false
- get:返回property的值得方法,默认是undefined
- set:为property设置值的方法,默认是undefined
Object.defineProperty(o,'age', {
value: 24, //值
writable: true, //可写
enumerable: true, //可枚举
configurable: true //可删除
});
const a= {};
Object.defineProperty(a, "b", {
set:function(newValue){
console.log("你要赋值给我,我的新值是"+newValue);
},
get:function(){
console.log("你取我的值");
return 2; //注意这里,我硬编码返回2
}
})
Object.getOwnPropertyDescriptor(O,property)
这个方法用于获取defineProperty方法设置的property 特性
const props = Object.getOwnPropertyDescriptor(o, 'age');
console.log(props); //Object {value: 24, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyNames
获取所有的属性名(方法也是属性),不包括prototy中的属性,返回一个数组
console.log(Object.getOwnPropertyNames(o)); //["age", "sex"]
例子中可以看到prototype中的name属性没有获取到
说明:原型上的属性都不会获取到,如Object.create方法创建的属性都不会返回。
Object.keys()
和getOwnPropertyNames方法类似,但是获取所有的可枚举的属性,返回一个数组
console.log(Object.keys(o)); //["age"]
说明:即enumerable属性为true的才可以被查询到,正常通过点方式增加的属性都是可枚举的。
Object.preventExtensions(O) / Object.isExtensible
方法用于锁住对象属性,使其不能够拓展,也就是不能增加新的属性,但是属性的值仍然可以更改,也可以把属性删除,Object.isExtensible用于判断对象是否可以被拓展
Object.seal(O) / Object.isSealed
方法用于把对象密封,也就是让对象既不可以拓展也不可以删除属性(把每个属性的configurable设为false),单数属性值仍然可以修改,Object.isSealed由于判断对象是否被密封
Object.freeze(O) / Object.isFrozen
终极神器,完全冻结对象,在seal的基础上,属性值也不可以修改(每个属性的wirtable也被设为false)
总结陈词
本篇文章带大家认识一下JavaScript
对象,更多JS知识,欢迎阅读《JS权威指南》等书籍。
JavaScript
真的就挺强大的,可以多花一些时间去挖掘它,也让我们继续见证了 JavaScript 的博大精深,后续会更新更多内容。
💗 如果觉得内容还可以,麻烦点个关注不迷路,您的鼓励是我创作的动力。