JavaScript对象及原型链
一、JS对象
1、对象定义
js对象是引用类型,用于存储键值对
2、对象创建
(1)直接量(字面量):适用于创建单个对象,当要创建多个对象会产生大量重复代码;
var person = {
name:'Tom',
age:20,
}
console.log(person.age);//20
(2)工厂模式:使用函数创建,一般也少用:
function createPerson1(name,age){
var obj = new Object();
obj.name = name;
obj.age = age;
return obj;
}
var person_1 = createPerson1('Tom',30);
console.log(person_1);//{name: "Tom", age: 30}
(3)构造函数模式:
- 系统自带构造函数:
var person_3 = new Object();
person_3.name = 'Tony';
console.log(person_3.name);
- 自定义构造函数:
function Teacher(name,age){
this.name = name;
this.age = age;
}
var teacher_1 = new Teacher('Tom',20);
console.log(teacher_1);//Teacher {name: "Tom", age: 20}
(4)new创建:使用默认构造函数new创建对象
var teacher_2 = new Object();
console.log(teacher_2);//{}
(5)Object.create()创建:括号内参数是对象的原型:
var a = Object.create(new Array(1,2,3,4,5));
console.log(a);//Array {}
console.log(a.length);//5
console.log(a.sort());//Array {0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
var b = Object.create(new String('null'));
console.log(b[3]);//l
3、对象方法:
(1)valueOf():返回当前对象的原始值,但是Math、Error对象是没有valueOf()方法的;
- Array:返回数组实例
- Boolean:返回布尔值
- Date:返回截止目前的毫秒值
- 函数:函数本身
- Number:数字值
- Object:对象本身
- String:字符串本身
(2)toString()方法:返回当前对象对应的字符串格式:
- Array:将数组转换为字符串并连接起来使用逗号分隔
- Boolean:true返回"true",否则"false"
- Date:返回日期文本表示样式
- Error:返回一个包含错误信息的字符串
- 函数:返回function functionName() { [native code] }
- Number:范湖数字的文本表示样式
- String:返回String对象值
- Object:返回object Object
(3)valueOf和toString的区别:
- valueOf偏向于计算,toString偏向于显示
- 进行强制转换字符串时优先调用toString,强转为数字时优先调用valueOf()
- 在有运算符的情况下,valueOf()的优先级高于toString()
4、对象属性
(1)定义和修改属性Object.defineProperty():
- 语法:Object.defineProperty(obj, prop, descriptor)
- 参数:obj 需要定义属性的对象;obj 需要定义属性的对象;descriptor 需被定义或修改的属性的描述符,是一个对象形式;
var person = {
name: 'yourname',
age: 10
};
Object.defineProperty(person, "sex", {
value: "male",
enumerable: false //不可枚举
});
(2)检测属性:
- in:检查一个属性是否属于某个对象,包括继承来的属性;
var obj = {
name:'Tom',
age:20
}
console.log('age' in obj);//true
console.log('say' in obj);//false
- hasOwnProperty():检查一个属性是否属于某个对象自有属性,不包括继承来的属性;
var person = {
name:'yourname',
age:10
};
person.__proto__.LastName = "deng"; //让person继承一个LastName属性
Object.defineProperty(person, "sex", {
value: "male",
enumerable: false //不可枚举
});
console.log(person.hasOwnProperty('name')); //true
console.log(person.hasOwnProperty('sex')); //true
console.log(person.hasOwnProperty('toString')); //false
console.log(person.hasOwnProperty('LastName')); //false
- propertyEnumerable():是hasOwnProperty()的增强版,检查一个属性是否属于某个对象自有属性,不包括继承来的属性,且该属性可枚举;
var person = {
name:'yourname',
age:10
};
person.__proto__.LastName = "deng"; //让person继承一个LastName属性
Object.defineProperty(person, "sex", {
value: "male",
enumerable: false //不可枚举
});
console.log(person.propertyIsEnumerable('name')); //true
console.log(person.propertyIsEnumerable('sex')); //false
console.log(person.propertyIsEnumerable('toString')); //false
console.log(person.propertyIsEnumerable('LastName')); //false
(3)存取器属性getter、setter,可以替代属性值
- 由getter、setter定义的属性成为存取器属性
- 一般仅一个值的属性为数据属性
- 设置存取器属性的值使用setter,可写
- 查询存取器属性值用getter,可读
myObj.b = 5;
console.log(myObj.b); //3,属性打印仍然为3
var myObj = {
a: 2,
get b() {
if(this._b_ == undefined) return 3;
else return this._b_;
},
set b(val) {
this._b_ = val;
}
};
console.log(myObj.b); //3
myObj.b = 100;
console.log(myObj.b); //100
var myobj = {
//数据属性
myname:'yourname',
birthday:'1983-05-17',
get myage(){
return (new Date().getFullYear()) - new Date(this.birthday).getFullYear();
},
set myage(value){
this.birthday = value;
}
};
myobj.myage = '1998-08-22';//相当于调用了set myage(value)方法
console.log(myobj.myage);//相当于调用了个get myage()方法
(4)删除属性:delete
- 严格模式下,不能删除变量,但是能够删除对象内部的属性
- 仅能删除自有属性,不能删除继承属性
- 删除成功或删除了不存在属性返回true
- 删除继承属性时,返回的是true,但只是表面删除,其原型中该属性依然存在
5、序列化对象(对象与字符串的转换)
(1)对象转换成字符串:JSON.stringify()
(2)字符串转换为对象:JSON.parse()——此时的字符串要是JSON格式
var obj = {
name:'Tom',
age:20,
classroom:[
'0001',
'0121',
'0010'
]
}
var str = JSON.stringify(obj);
console.log(str,typeof str);
//{"name":"Tom","age":20,"classroom":["0001","0121","0010"]} string
var obj_1 = JSON.parse(str);
console.log(obj_1,typeof obj_1);
//{name: "Tom", age: 20, classroom: Array(3)} "object"
二、原型链
1、原型
(1)定义:是function对象的一个属性,定义了构造函数构造出来的公共祖先,通过该构造函数创建的对象,可以继承他的原型的属性和方法;原型也是对象;
(2)原型属性:
- prototype:构造函数的原型
- proto:指向构造函数的prototype,是创建构造函数时自动添加的对象
- constructor:指向构造函数本身
声明一个函数,则这个函数默认会有一个属性叫 prototype ;而且浏览器会自动按照一定的规则创建一个对象, 这个对象就是这个函数的原型对象,prototype属性指向这个原型对象; 这个原型对象有一个属性叫constructor 执行了这个函数
- 注意:原型对象默认只有属性–constructor。 其他都是从Object继承而来,暂且不用考虑。
function Person (username){
this.username = username;
this.say = function(){
console.log(this.username);
}
}
console.log(Person.__proto__ === Function.prototype);//true
console.log(Person.prototype.__proto__ === Object.prototype);//true
代码图解:
2、原型的使用:
- 可以在原型上扩展属性或者方法,包括系统内置构造函数;
- 原型给了我们一个修改内置构造函数的接口和机会
- 方法劫持call、apply、bind
示例:
function Person(username){
this.username = username;
}
//在原型上增加属性或方法
Person.prototype.say = function(){
console.log('11111');
}
Person.prototype.a = 200;
var p1 = new Person('mei');
console.log(p1);
p1.say();//11111
console.log(p1.a);//200
//修改系统内置的构造函数的属性或方法
var arr = new Array(1,2,3);
Array.prototype.sum = function(){
console.log(this);
var sum = 0;
for(var i = 0;i<this.length;i++){
sum +=this[i];
}
return sum;
}
Array.prototype.max = function(){
console.log(this);
var max = this[0];
for(var i = 0;i<this.length;i++){
(max < this[i])&&(max = this[i]);
}
return max;
}
console.log(arr.sum());//6
var arr1 = [100,200,300];
console.log(arr1.sum());//600
console.log(arr1.max());//300
var str = new String('aaa');
console.log(str);//String{'aaa'}
//Math就是一个对象,没有构造函数,不需要new
console.log(Math.max(1,2,3,9,0));//9
//改变指向
console.log(Math.max.apply(arr1,arr1));//300
- 改变原型对象:当改变了原型对象,constructor将不会再指向构造函数,需要指定constructor指向的对象
function Person(){}
console.log(Person.prototype);
//为构造函数指定新的原型对象
//当改变了原型对象,constructor将不会再指向构造函数,需要指定constructor指向的对象
Person.prototype = {
name:'Tom',
age:20,
constructor:Person
}
console.log(Person.prototype.constructor);//Person构造函数
- 查看原型
构造函数名.prototype
对象.proto
function Person(name,age){
this.name = name;
this.age = age;
}
console.log(Person.prototype);//指向Person构造函数
Person.prototype = {
sleep:function(){
console.log('sleeping');
}
}
//更改了原型指向,没有再指定constructor
console.log(Person.prototype);//指向新的原型对象
var p1 = new Person('Tom',20);
console.log(p1.__proto__);//查看原型(此时原型是新的原型)
p1.sleep();//调用新的原型对象中的方法
- 通过原型创建对象
(1)不推荐的创建属性方法:在原型上创建属性,适宜在原型上创建方法
function Person(){}
Person.prototype.name = 'Tom';
Person.prototype.age = 20;
var p1 = new Person();
console.log(p1.name);//Tom
p1.__proto__.name = 'Tony';
//改变了原型上的属性值
console.log(p1.name);//Tony
var p2 = new Person();
//当改变了原型上的属性值后,再通过构造函数创建的对象访问该原型,
//该属性值是上个对象更改后的属性值
console.log(p2.name);//Tony
(2)在原型上创建方法:将相同的函数创建在原型上
function Person(name,age){
this.name = name;
this.age = age;
}
//将相同的函数定义在原型上
Person.prototype.sleep = function(){
console.log('sleep');
}
var p1 = new Person('Tom',10);
p1.sleep();
var p2 = new Person('Tony',20);
p2.sleep();
(3)动态创建原型上的方法和属性:通过判断属性的类型是函数还是普通属性,将函数创建在原型上,将普通属性创建在构造函数里
function Person(name,age){
this.name = name;
this.age = age;
if(typeof this.sleep == 'function'){
Person.prototype.sleep = function(){
console.log('sleeping');
}
}
}
var p1 = new Person('Tom',10);
p1.sleep();
var p2 = new Person('Tony',20);
p2.sleep();
3、原型链
- 原型链
(1)每个对象都从它的原型继承属性,直到Object的原型指向null
(2)原型链是将一个个原型连起来,形成一条原型继承的链,原型链的顶端是Object
下面来看一个简单的原型链:
function Person(){
}
var p1 = new Person();
console.log(p1.__proto__);
-
使用原型链实现继承关系
(1)原型链是继承的基本思想,它例用原型让一个引用类型继承两一个引用类型的属性和方法;
(2)改变某个构造函数的原型指向,可以控制它继承自哪个对象
例如:
function GrandParent(){
this.name = 'A';
}
function Parent(){
this.name = 'B of A';
}
Parent.prototype = new GrandParent();
function Child(){
this.name = 'C of B';
}
Child.prototype = new Parent();
var child = new Child();
console.log(child.__proto__);
该例的详细继承过程的原型链如下所示:
4、new操作
- 创建新对象,调用构造函数,同时在堆中开辟空间将对象存入进去,并将地址保存在变量中
- 将函数的上下文对象(作用域)中的this指向了该对象
- 执行构造函数中的代码,通过this来添加属性和方法
- 返回对象
比如:
function Person(name,age){
this.name = name;
this.age = age;
//创建对象中的方法
this.say = function(){
return this.name;
}
return 100;
}
//创建Person的实例化对象
var person1 = new Person('Tom',18);
console.log(person1);
//Person {name: "Tom", age: 18, say: ƒ}
new过程图解: