目录
一、对象定义的三大方法
1、直接以对象类型定义
此方法即是按照我们所理解的对象形式,直接按照{}组织定义,如下:但是这样构造出来的对象无法调用其原型prototype也无法基于原型链创造孩子。
var test={
aIndex:'1',
bIndex:0,
cIndex:'hello'
displayType : function() {
console.log(this.aIndex);
}
};
2、通过函数构建
函数方法就是将定义的函数作为对象去处理,如下:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year; //this引用,定义属性的属性值
//也可以定义方法
this.displayType = function() {
console.log(this.make);
}
}
var mycar = new Car("Eagle", "Talon TSi", 1993); //创建对象实例
mycar.displayType(); //调用方法
3、通过Object构造器自定义
利用原型对象Object的构造器进行定义,如下:但是这样构造出来的对象无法调用其原型prototype也无法基于原型链创造孩子。
var myObj = new Object();
var str = "myString",
rand = Math.random(),
obj = new Object();
//点 法
myObj.type = "Dot syntax";
myObj.name = 10;
//索引 法
myObj["date created"] = "String with space";
myObj[str] = "String value";
myObj[rand] = "Random Number";
myObj[obj] = "Object";
myObj[""] = "Even an empty string";
console.log(myObj);
二、对象属性名和属性值的获取与删除
1、获取方法
对象的属性名相对来说好获取,属性值一般是在获取到属性名后通过obj[属性名]来获得,但也有方法可以直接获取,常用方法如下:
- for...in 循环获取属性名 【注意:forEach只能用来遍历数组而不可以遍历对象】
var obj = {a:1, b:2, c:3}; for (var prop in obj) { console.log("obj." + prop + " = " + obj[prop]); }
- Object.keys(o) 获取对象中所有属性名组成的数组,Object.values(o) 则用于获取对象中所有属性值组成的数组
//keys const object1 = { a: 'somestring', b: 42, c: false }; console.log(Object.keys(object1)); // expected output: Array ["a", "b", "c"] //values var obj = { 0: 'a', 1: 'b', 2: 'c' }; console.log(Object.values(obj)); // ['a', 'b', 'c']
- Object.getOwnPropertyNames(o) 该方法返回对象
o
自身包含的所有属性 (无论是否可枚举) 的名称的数组。var arr = ["a", "b", "c"]; console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"] // 类数组对象 var obj = { 0: "a", 1: "b", 2: "c"}; console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"] // 使用 Array.forEach 输出属性名和属性值 Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) { console.log(val + " -> " + obj[val]); }); // 输出 // 0 -> a // 1 -> b // 2 -> c //不可枚举属性,create是利用了继承的方法,第二个参数中的方法将继承到第一个空对象参数中 var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; }, enumerable: false } }); my_obj.foo = 1; console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]
注意~:
上述方法获取到的属性都是对象本身中的,不包含原型对象/原型链中定义的其他属性和方法
下文会详细介绍原型对象和原型链
2、属性删除
delete关键字删除即可:
//Creates a new object, myobj, with two properties, a and b.
var myobj = new Object;
myobj.a = 5;
myobj.b = 12;
//Removes the a property, leaving myobj with only the b property.
delete myobj.a;
3、getters和setters
一个 getter 是一个获取某个特定属性的值的方法。一个 setter 是一个设定某个属性的值的方法。在对象中定义getter和setter,可以封装对象,实现OOP编程,减少内部成员的直接暴露。
关于getter和setter的定义方式有如下两种:
- 直接构造访问器属性:
var o = { a: 7, get b() { return this.a + 1; }, set c(x) { this.a = x / 2 } }; console.log(o.a); // 7 console.log(o.b); // 8 o.c = 50; // 可以setter修改 console.log(o.a); // 25
- 使用Object.defineProperty方法
var book = { _year: 2004, edition: 1 } Object.defineProperty(book, "year", { get: function () { return this._year; }, set: function (newValue) { if (newValue > 2004) { this._year = newValue; this.edition += newValue - 2004; } } }); console.log(book._year + "." + book.edition); // 2004.1 book._year = 1900; console.log(book._year + "." + book.edition); //1900.1 book.year = 2005; console.log(book.year + "." + book.edition); //2005.2
三、this引用对象
this指向当前对象,一般应用于与HTML语言结合使用时。我们可以将每个标签理解为一个对象,其中定义的属性都是对象中可获取的属性名和值,那么就可以与JS方法绑定应用啦~看个例子:
// 1、this指向标签自身,以参数形式传入绑定的方法中
function validate(obj, lowval, hival) {
if ((obj.value < lowval) || (obj.value > hival)) {
alert("Invalid Value!");
}
}
<input type="text" name="age" size="3"
onChange="validate(this, 18, 99)">
// 2、this.form指向父元素form(name="myForm"),this.form.text1指向父元素下name为text1的元素,所以在获取子元素时,name属性作为元素的键值
<form name="myForm">
<p><label>Form name:<input type="text" name="text1" value="Beluga"></label>
<p><input name="button1" type="button" value="Show Form Name"
onclick="this.form.text1.value = this.form.name">
</p>
</form>
四、对象的继承
对象的继承,是基于原型(链)的继承。
1、原型对象和原型链
JavaScript 是动态的,本身不提供一个 class
的实现。即便是在 ES2015/ES6 中引入了 class
关键字,但那也只是语法糖(语法的简洁表达),JavaScript 仍然是基于原型的。所以当谈到继承时,JavaScript 只有一种结构:对象。
每个实例对象(object)都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null
。这种对象间的层级关系就构成了原型链, 且原型链的终端是null。
像数组(array)、字符串(string)这两个JS基本数据类型,用typeof 关键字打印出的其类型为object
var s="ssss";
console.log(typeof s); //object
var arr=[1,2,3];
console.log(typeof arr); //object
这是因为数组和字符串的原型对象为Object,是从object对象中继承而来的。
所以如果要准确区分出数据类型究竟是数组、字符串还是对象,需要用Object.prototype.toString.call(obj)方法
console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]
console.log(Object.prototype.toString.call(new Date));//[object Date]
console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
function Person(){};
console.log(Object.prototype.toString.call(new Person));//[object Object]
2、基于原型链的继承方法
- new方法
比如以下两个对象doSomething 和 doSomeInstancing之间就存在继承,其中 doSomeInstancing是通过new 一个doSomething对象的实例构造出来的,会继承doSomething对象中的属性和方法
function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );
查看doSomeInstancing对象结构,如下:
{
prop: "some value",
__proto__: {
foo: "bar",
constructor: ƒ doSomething(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
}
可见原型链由下至上为 doSomeInstancing -> doSomething -> Object
- 使用Object.create()方法基于原型创建新对象
该方法是基于现有对象为原型创建新的对象,语法参数如下:
Object.create(proto)
Object.create(proto, propertiesObject)
分别举一个例子:
// 无第二个参数
var a = {test: 1};
var b = Object.create(a); //原型链为: b ---> a ---> Object.prototype ---> null
console.log(b.test); // 1 (从对象a中继承而来)
//有第二个参数
var o = Object.create(null, {
foo: {
writable: true,
configurable: true,
value: 'hello'
},
bar: 'myBar',
});
3、prototype的使用
下述例子中,通过 prototype
属性给之前定义的Car对象类型增加color属性。这为继承Car类型的所有对象都增加了color属性。但如果子对象自己又定义了color属性的属性值,则会覆盖之前继承而来的color属性值。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year; //this引用,定义属性的属性值
//也可以定义方法
this.displayType = function () {
console.log(this.make);
}
}
Car.prototype.color = "black"; //原型对象加属性
var mycar = new Car("Eagle", "Talon TSi", 1993); //Car类型实例1
mycar.color = "white";
console.log(mycar.color); //输出white
var t = new Car("Eagle", "Talon TSi", 1993); //Car类型实例2
console.log(t.color); //输出black color属性继承而来
五、对象的比较(从内存角度理解)
1、比较对象怪象
在 JavaScript 中 objects 是一种引用类型。两个独立声明的对象永远也不会相等,即使他们有相同的属性,只有在比较一个对象和这个对象的引用时,才会返回 true.
// 两个变量,两个具有同样的属性、但不相同的对象
var fruit = {name: "apple"};
var fruitbear = {name: "apple"};
fruit == fruitbear // return false
fruit === fruitbear // return false
如果为引用,
// 两个变量,同一个对象
var fruit = {name: "apple"};
var fruitbear = fruit; // 将 fruit 的对象引用(reference) 赋值给 fruitbear
// 也称为将 fruitbear“指向”fruit 对象
// fruit 与 fruitbear 都指向同样的对象
fruit == fruitbear // return true
fruit === fruitbear // return true
2、解释该现象
导致上述现象的主要原因是对象其实赋值给变量的不是值,而是内存地址。因此我们比较对象时不仅比较内容,还比较着变量地址。这就是对象作为引用类型的特别之处。
我们知道,JS中有七种基本数据类型(number string object等等),这七种基本数据类型又可以按照存储方式分为两大类型:原始类型和引用类型。这两个究竟有啥区别呢?
首先我们要了解,JS内存分为三大块:代码空间、栈空间、堆空间。而原始类型保存在栈空间,引用类型保存堆空间。这就导致我们定义变量时,Javascript引擎将自动把不同类型的变量分配到合适的空间内。
如,有以下代码:
解析后变量环境存储情况为: