前言
最近在廖雪峰的官方网站,看了一些Javascript的相关教程,感觉这套教程里的思路还是挺清晰明了的,该篇读书笔记就是将其中对象方面的内容进行了一个总结性的整理。
关于js中的对象
格式
var hero = {
name: 'hero',
sex: 'male' ,
'code-name': 'codename',
shoot: function () {
console.log(this.name + ' is shooting');
}
info: function () {
console.log('name:' + this.name + '\n'
+ 'sex:' + this['sex'] + '\n'
+ 'code-name:' + this['code-name']);
}
};
在javascript中,对象是以键值对的形式存在的,格式上:
- 格式和json有点类似,用
{}
括起来,属性和值之间用:
隔开,而非=
- 每组键值对(属性与值)之间用
,
隔开 - 如果属性名中包含特殊字符,比如
-
的话,属性名要用''
括起来 - 属性的值可以是一个函数
用法
调用
console.log('name:' + hero.name + '\n'
+ 'sex:' + hero['sex'] + '\n'
+ 'code-name:' + hero['code-name']);
hero.shoot();
hero.info();
- 直接用
变量.属性
或者变量['属性']
,如果属性名是用''
隔开的只能用变量['属性']
方式 - 如果属性是方法,也是用
变量.属性(参数..)
的方式调用。
属性增加删除
console.log(hero.height);//undefined
hero.height = 180;//添加属性
console.log(hero.height);//180
delete hero.height;//删除属性
console.log(hero.height);//undefined
添加的话,直接赋值即可。删除时,如果属性不存在也不会报错
判断属性是否存在
判断属性存在有两种方式
-
'属性名' in '变量'
, 返回true/false,存在true,不存在就是false。如果该属性是继承下来的属性那么同样返回 true -
hasOwnProperty
方法,返回 true/false,存在true,不存在就是false。只有是自身属性才返回true。
console.log('name' in hero);//true
console.log('height' in hero);//false
console.log('toString' in hero);//true
hero.hasOwnProperty('name');//true
hero.hasOwnProperty('height');//false
hero.hasOwnProperty('toString');//false
特性
_proto_ 和 Object.create()
将A对象的原型指向B对象,那么A对象就获得了B对象中的属性
var dva = {
name: 'D.VA',
height: 180,
}
dva.__proto__ = hero;
dva['code-name'];//codename
不过一般不直接使用对象的 __protp__
属性,部分浏览器不支持,通常的做法是使用 Object.create
方法
function createStudent(name) {
var s = Object.create(hero);
s.name = name;
return s;
}
var mercy = createStudent("mercy");
mercy['code-name'];//codename
new一个对象
js中 用 new
调用一个函数会返回一个新创建的对象,该函数中的 this
就指向这个新创建的对象。
function Map(name) {
this.name = name;
this.start = function () {
console.log('正在前往' + name);
}
}
var Dorado = new Map('多拉多');
Dorado.name;//多拉多
Dorado.start();//正在前往多拉多
var Numbani = new Map('努巴尼');
Numbani.name;//努巴尼
Numbani.start();//正在前往努巴尼
js中调用普通函数,会返回一个 undefined
原型链
当在js中访问某个对象的属性时(obj.xxx
)
- 首先会在改对象中查找该属性
- 如果第1步找不到该属性,就会从该对象的原型对象中查找(该对象
__proto__
属性指向的对象) - 找不到的话会一直找下去,直到找到
Object.prototype
,如果还没有就会返回undefined。
这样就形成了原型链
Dorado ----> Map.prototype ----> Object.prototype ----> null
Dorado.__proto__ === Map.prototype;//true
Map.prototype.__proto__ === Object.prototype;//true
Object.prototype.__proto__ === null;//true
Dorado.constructor === Map;//true
上面对象中 _Dorado_
和 _Numbani_
中的 _start_
属性一个函数,函数名和代码都相同,根据原型链,Dorado
和 Numbani
上找不到的属性,会从他们的原型上找,于是我们可以将 _start_
属性移动到 Map.prototype
上。
function Map(name) {
this.name = name;
}
Map.prototype.start = function () {
console.log('正在前往' + this.name);
};
create一个对象三步曲
- 创建构造函数
- 添加通用方法
- 封装构造函数
//第一步:创建构造函数(首字母一般大写,用于和普通函数区分)
function Map(props){
this.name = props.name || '训练靶场';
}
//第二步:添加通用方法(方法写在构造函数prototype中)
Map.prototype.start = function () {
console.log('正在前往' + this.name);
}
//第三步:封装构造函数(把构造函数封装一下,省的调用的时候每次都写new)
function createMap(props){
return new Map(props || {});
}
var Trainrang = createMap();
Trainrang.start();//正在前往训练靶场
var Nepal = createMap({
name : '尼泊尔'
});
Nepal.start();//正在前往尼泊尔
原型继承
当一个对象中的某个属性找不到时,会在原型上去找,所以可以通过原型在js中完成继承操作。
如果想创建Map的一个子类FestvialMap,那么FestvialMap的原型链应该是:
FerguGym ----> FestivalMap.prototype ----> Map.prototype ----> Object.prototype ----> null
如上图,最简单的方式就是将 FestivalMap.prototype.__proto__ = Map.prototype
,不过一般不会直接操作对象的__proto__
属性。于是采用下面的方案
- 创建一个空函数
M
,这时new M()
对象的__proto__
属性就指向了M.prototype
- 将
M.prototype
赋值为Map.prototype
,这时new M()
对象的__proto__
就指向了Map.prototype
- 将
FestivalMap.prototype
赋值为new M()
,这时FestivalMap.prototype
对象的__proto__
就指向了Map.prototype
- 由于现在的
FestivalMap.prototype
就是刚才新创建的new M()
,将他的constructor
属性指向他本身
function FestivalMap(props) {
Map.call(this, props);
}
function createFestivalMap(props) {
return new FestivalMap(props || {});
}
//创建一个空函数 M ,这时 new M() 对象的 __proto__ 属性就指向了 `M.prototype`
function M() {
}
//将 M.prototype 赋值为 Map.prototype,这时 new M() 对象的 __proto__就指向了 Map.prototype
M.prototype = Map.prototype;
//将 FestivalMap.prototype 赋值为 new M(),这时 FestivalMap.prototype 对象的 __proto__就指向了 Map.prototype
FestivalMap.prototype = new M();
//由于现在的 FestivalMap.prototype 就是刚才新创建的 new M(),将他的 constructor 属性指向他本身
FestivalMap.prototype.constructor = FestivalMap;
FestivalMap.prototype.type = function () {
console.log('特殊地图:' + this.name)
}
var FerguGym = createFestivalMap({
name: '弗格体育场',
});
console.log(FerguGym.name);//弗格体育场
FerguGym.start();//正在前往弗格体育场
FerguGym.type();//特殊地图:弗格体育场
封装这几步
function inherits(Child,Parent){
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
class继承
在ES6新增了用关键字 class
来定义类的方式,和原型类继承原理是一样的,只是简化了代码。
class Map {
//构造方法
constructor(name) {
this.name = name || '训练靶场';
}
//相当于之前的 Map.prototype.start = function(){...},前面不加function
start() {
console.log('正在前往' + this.name);
}
}
var LijiangTower = new Map('漓江塔');
LijiangTower.start();//正在前往漓江塔
class FestivalMap extends Map {
constructor(name) {
//调用父类构造方法
super(name);
}
type() {
console.log('特殊地图:' + this.name);
}
}
var FerguGym = new FestivalMap('弗格体育场');
FerguGym.start();//正在前往弗格体育场
FerguGym.type();//特殊地图:弗格体育场