js之对象(经典)
一、对象的定义:
对象是JavaScript的一个基本数据类型,是一种复合值,它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值。即属性的无序集合。
二、对象的创建(多种方法)
1、对象直接量 / 字面量
var obj = {
name: 'lyl',
age: 18
}
console.log(obj.name); // lyl
2、构造函数:
(1)、系统自带的的, eg: new Object(), Array(), Number(),Boolean(), Date()...
var obj = new Object();
obj.name = 'lyl';
console.log(obj.name); //lyl
(2)、自定义的:为了和普通函数区分,首字母大写,采用大驼峰式写法(普通函数采用小驼峰式写法)

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

自定义构造函数的基本构造原理:
首先,文字理论解释一番,其实一切的关键全在与new这个操作符上,用new和不用new返回的结果大相径庭。不用new,则Obj('lyl')根本就是一个函数的正常执行,没有返回值,则默认返回undefined,而是用new操作符后js引擎就会将该函数看作构造函数看待,经过内部的一系列隐士操作,返回值就是一个对象了。
看下如下demo:
demo1:用new和不用new的区别演示:

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

demo2 用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 = 'lyl';
this.age = 18; // 隐士操作
// return this;
} var person1 = new Person();
console.log(person1.name); // lyl
person1.say(); //I am saying

上述两步理论的验证:
第一步:现在函数内的this是一个原型为Person.prototype, 构造函数为Person的对象

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

第二步:隐士的将创建的对象this通过return返回
由以上一些代码,我们已经可以看出返回的是满足条件的对象,现在我们创建对象时不用new,并显示的模拟这两步隐士操作来验证(我们不用new则两步隐士操作就不会产生)

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

p.s. 关于显示返回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、增: 所谓增添一个对象的属性,就是直接对该属性进行赋值操作即可,这就相当于为该对象添加了一个新属性,而打印未添加的属性,浏览器不会报错,而是会打印出undefined
var obj = {};
console.log(obj.name); //undefined (不会报错)
obj.name = 'lyl';
console.log(obj.name); // lyl
2、删:我们通过delete操作符来删除一个对象的属性
var obj = {
name : 'lyl'
};
console.log(obj.name); //lyl
delete obj.name;
console.log(obj.name); //undefined
3、改: 修改一个对象的属性是最简单的了,直接通过赋值操作赋予其其他的值即可
var obj = {
name: 'lyl'
};
console.log(obj.name); // lyl
obj.name = 'obj';
console.log(obj.name); // obj
4、查:查询一个对象的属性值有两种方法

var obj = {
name: 'lyl'
};
// 第一种方法
console.log(obj['name']); //lyl
// 第二种方法
console.log(obj.name); // lyl
//p.s.最本质的是第一种方法,因为在使用第二种方法时,后台自动将其转换为第一种字符串的形式来查询

p.s.以上的增、删、改三种操作都只是针对当前对象的属性进行操作,而不会影响到当前对象的原型的属性。
而查询是先看看当前对象本身是否设置了该属性,如果当前对象未设置该属性,则再看该对象的原型中是否设置了该属性,若两者都没有,则返回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';
str.len = 4;
console.log(str.len); // undefiend
//???
关键在于‘销毁’一词上,解释如下:

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

一个易错的坑:
// 总之记得'原始值包装类''销毁'这两句话就行
var str = 'abcd';
str.lenth = 2;
console.log(str); // ab or abcd ? answer is abcd
console.log(str.length); // 2 or 4 ? answer is 4
五、原型:
1、原型的定义: 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过改构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
2、利用原型特点和概念,可以提取共有属性。将一类对象的共有属性提取出来,放到该类对象的原型中,从而不需要每次用new操作符时都重新定义一遍该共有属性。
如下,定义一个Person构造函数,而属于Person多构造对象共有的属性方法,则定义到Person的原型中

Person.prototype = {
eat: function (food) {
console.log('I have eated ' + food);
},
sleep: function () {
console.log("I am sleeping");
}
}
// 人的构造函数
function Person (name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person('lyl', 18);
console.log(person1.name); //lyl
person1.eat('apple'); //I have eated apple

3、如何查看原型:
之前是不允许我们查看构造函数的原型的,但后来提供了一个可查看构造函数原型的接口:隐士属性__proto__(其实我们能够访问原型的属性,或者说继承原型,靠的就是__proto__属性连接着构造函数和原型,可以说没有__proto__属性的存在,就无法实现原型的继承)
(1)、首先我们先说明一下__proto__这个接口是存放到哪里的
看过以上对象创建过程的都应该知道在用new创建一个对象时,内部会隐士自动创建一个this的对象,进过一系列处理后再隐士将this对象返回。而__proto__就对于隐士创建的this对象中,如下代码:

// 原型
Person.prototype = {
say: function () {
console.log("I am saying ");
},
play: function () {
console.log("I am playing");
}
}
// 构造函数
function Person (name) { // var this = Object.create(Person.prototype);
// p.s.在隐士创建的this对象中存在一个属性,即__proto__,该属性存储了Person.prototype this.name = name; // return this;
}
// 对象的创建
var person1 = new Person('lyl');
// 打印原型
console.log(person1.__proto__);

(2)、如何查看原型:直接通过new操作符创建的对象访问__proto__属性即可,如上代码演示
4、如何查看对象的构造函数,我们通过属性constructor来查看:
contructor属性位于构造函数的原型中,其中存储的是构造函数信息,所以在不知道原型的情况下,由原型继承原理,我们可以用实例对象来直接访问constructor,即获取创建该实例的构造函数
function Person () {
this.name = 'myName';
this.age = 18;
}
var person = new Person();
console.log(person.constructor); // function Perso(){...}
六、原型链:
1、定义:顾名思义,原型链就是将一个个原型串连起来,形成一条原型继承的链子。
2、原型链的构成:
如下代码例子, Child继承Parent, Parent继承GrandParent, 而GrandParent没有自定义原型,所以默认为原型链的最顶端new Object();
(为什么Object为最顶端,因为Object.prototype为null,为null是没有原型的)

// 原型链: Child -> new Parent() -> new GrandParent() -> new Object();
function GrandParent() {
this.name = 'GrandParent';
this.a = 3;
}
Parent.prototype = new GrandParent();
function Parent() {
this.name = 'parent';
this.b = 2;
}
Child.prototype = new Parent();
function Child() {
this.name = 'child';
this.c = 1;
} var child = new Child();
console.log(child); // Child {name: "child", c: 1} console.log(child.a); // 3
console.log(child.b); //2
console.log(child.c); //1

3、原型链的增删改查:
使用如上的原型链说明, Child -> new Parent() -> new GrandParent() -> new Object(), 实例对象为child
(1)、增:
为child实例对象添加属性,总是添加为其自己本身的属性,为对原型和原型链是没有影响的。(再具体说即对和其相同构造函数构造的实例对象无法造成影响,以下说法同此)
(2)、删:
使用delete操作符只能删除child实例对象自己本身的属性,而无法删除由原型继承而来的属性
(3)、改:
分两种情况:
若修改的属性为继承自原型的,且值类型为原始值,则仅仅修改的是该实例对象的属性,对原型无法造成影响。
若修改的属性为继承自原型的,属性值类型为引用值,则对引用值的修改又分两种情况:
第一种是直接对该修改的属性赋值 => 此情况仅仅修改的是实例对象的该属性,无法对原型造成影响。
第二种是对该修改的属性添加内容或去除内容,而不是对其重新赋值 => 此情况会对原型造成影响。如下例子:

Person.prototype = {
has: [1, 2, 3]
}
function Person () {
this.name = 'lyl';
}
var person1 = new Person();
var person2 = new Person();
person1.has.push(4);
// person1 和 person2都改变了,因为person1的修改影响到了原型,进而影响到了另一个实例对象
console.log(person1.has); //[1, 2, 3, 4]
console.log(person2.has); // [1, 2, 3, 4]

(4)、查:
查询过程如下,首先看构造函数中是否有要查询的属性,若有,则直接返回,若没有,则看其原型有没有要查询的属性,若没有,则再看原型的原型上是否有要查询的属性,以此顺序在原型链上查询,若一直到原型链顶端后仍没有要查询的属性,则返回undefined
p.s.理解了在原型链上的增删改查后,自然就能理解在原型上的增删改查了,只要把在原型上的增删改查当成只有一个原型的很短的原型链即可。
4、绝大多数对象最终都会继承自Object.prototype
为什么事绝大多数呢?因为null,和undefined是没有原型的,上文有详细提到
七、对象继承史
由于之前已经总结过这些,所以此处直接就粘链接了: http://www.cnblogs.com/Walker-lyl/p/5592048.html
八、命名空间:
我们可以利用对象创建命名空间来管理变量,防止污染全局,适用于模块开发,如下简单的小demo:

var workSpace = {
person1: {
name: 'one',
age: 18
},
person2: {
name: 'two',
age: 20
}
}
// 这样两个人虽然有同名变量,但不会相互影响,因为位于不同命名空间
// 访问第一个人的姓名
console.log(workSpace.person1.name); // one
console.log(workSpace.person2.name); //two

九、实现类似jquery中的链式调用: return this;
如下小demo,其中的this不懂的没关系,上面会说,你只要把次demo中的this当成person对象就行

var person = {
foodCount: 10,
eat: function () {
this.foodCount--;
return this;
},
buy: function () {
this.foodCount++;
return this;
},
print: function () {
console.log(this.foodCount);
}
}
// foodCount初始值为10, 在连续吃了三次后,变为7
person.eat().eat().eat();
person.print(); //7

十、对象的枚举:
1.obj.hasOwnProperty('prop');
该方法的作用是来判断对象obj的自身属性中是否含有属性prop,
自身属性是在构造函数中生成的或者实例对象后来自己添加的,而继承属性则是从原型上继承的属性,
所以该方法就是判断该属性是从原型继承来的还是自身的。
作用: 遍历一个对象的所有自身属性,因为es5中的对象遍历是默认打印包括继承自原型的属性的,demo如下

Person.prototype.age = 18;
function Person () {
this.name = 'lyl';
}
var person = new Person();
// 未用hasOwnProperty
// print:
// lyl
for(var prop in person) {
console.log(person[prop]);
}
// 使用hasOwnProperty
// print:
// 18 lyl
for(var prop in person) {
if(person.hasOwnProperty(prop)) {
console.log(person[prop]);
}
}

2、prop in obj;
in操作符用来判断该对象obj上是否有该属性prop,prop既可以是自身属性,也可以是继承属性,如下demo

Person.prototype.age = 18;
function Person () {
this.name = 'lyl';
}
var person = new Person();
console.log('age' in person); // true
console.log('name' in person); //true
delete person.name;
console.log('name' in person); //false

3、object instanceof Object;
instanceof操作符用来判断object实例对象是否为Object构造函数创建的,如下demo

Person.prototype.age = 18;
function Person () {
this.name = 'lyl';
}
var person = new Person();
console.log(person instanceof Person); // true
console.log(new Object() instanceof Person); //false

十一、this基本介绍:
1、函数预编译过程 this —> window
2、全局作用域里 this —> window
3、obj.func(); func()里面的this指向obj), 可以这样理解,谁调用func,则this就指向谁
4、call/apply 可以改变函数运行时this指向,
(1)、call用法:
func.call(要改变后的this, arg1, arg2, ... );
(2)、apply用法:
func.apply(要改变后的this, [arg1, arg2, arg2]);
(3)、apply和call共同点:都是改变this指向
apply和call不同点:传参形式不同,call是将参数一个个传进来,而apply是将所有参数存进一个数组中,然后将该数组传
如下demo:

// demo1
function demo1() {
console.log(this);
}
// demo1() <==> this.demo1(); <==> window.demo1()
demo1(); // window
// demo2
var demo2 = {
retThis: function () {
console.log(this);
}
}
demo2.retThis(); // demo2 = {...}
// call / apply改变this
demo1.call(demo2); // demo2 = {}
demo2.retThis.call(window); // window

十二、对象的克隆:
你可能回想,直接用等号赋值不久完成克隆了吗?这还用说,但你忽略了对象是一个引用值,赋值操作赋的是该对象的引用,然后会产生很坏的影响。
举例来说,将obj1赋值给obj2,然后我们向obj2中添加了一个属性,然后我们会惊喜的发现obj1中也有了此刚刚向obj2中添加的属性,但我obj1并不需要该属性啊,则造成obj1不开心了,为了让obj1开心起来,我们向下看,嘿嘿:
对象克隆,分为浅克隆和深克隆,而上边的直接赋值的克隆操作为浅克隆,为什么称为浅克隆呢?因为,克隆的和被克隆的对象在克隆操作完成后,指向同一个地址引用,改变其中一个(注意:此处的改变为增加或删除对象的属性,而不是为该对象重新赋值一个对象),另一个也会改变,而深克隆则不会产生此现象。
深克隆/深拷贝code如下,这是我从网上搜到的一个目前我见到的最完整最简短的code,直接贴上:

// 对象的深度克隆(Array / obj /...)
function deepClone(obj) {
var str, retObj = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};
if(typeof obj !== 'object') {
return;
}else if(window.JSON) {
str = JSON.stringify(obj);
retObj = JSON.parse(str);
}
else {
for(var prop in obj) {
retObj[prop] = typeof obj[prop] === 'object' ? deepClone(obj[prop]) : obj[prop];
}
}
return retObj;
}

------------------------------------------end 至此基本包含了对象的i基本知识点
js之对象(经典)的更多相关文章
- js中的经典案例--简易万年历
js中的经典案例--简易万年历 html代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8& ...
- js定义对象的几种容易犯的错误
//js定义对象的几种容易犯的错误function Person() { getName = function (){ console.info(1); }; return this;}//Perso ...
- 模仿console自写函数打印js的对象
本以为写个递归函数就可以将js的对象打印出来. 当然第一个想到的估计是JSON.stringify() 这个函数.但这个函数打印到浏览器 显示效果不友好.最友好的显示肯定是 控制台打印咯. 结果尝试打 ...
- js自定义对象
一,概述 在Java语言中,我们可以定义自己的类,并根据这些类创建对象来使用,在Javascript中,我们也可以定义自己的类,例如定义User类.Hashtable类等等. 目前在Javascrip ...
- Atitit 跨平台异常处理(2)--------异常转换 -----java c# js异常对象结构比较and转换
Atitit 跨平台异常处理(2)--------异常转换 -----java c# js异常对象结构比较and转换 { "@type":"java.lang.Runti ...
- js中对象使用
简单记录javascript中对象的使用 一.创建对象 //创建一个空对象 var o={}; //创建一个含有两个属性的对象,x.y var o2={x:12,y:'12',name:'JS'}; ...
- JavaScript学习06 JS事件对象
JavaScript学习06 JS事件对象 事件对象:当事件发生时,浏览器自动建立该对象,并包含该事件的类型.鼠标坐标等. 事件对象的属性:格式:event.属性. 一些说明: event代表事件的状 ...
- [转]JS中对象与字符串的互相转换
原文地址:http://www.cnblogs.com/luminji/p/3617160.html 在使用 JSON2.JS 文件的 JSON.parse(data) 方法时候,碰到了问题: thr ...
- JS中对象与字符串的互相转换
在使用 JSON2.JS 文件的 JSON.parse(data) 方法时候,碰到了问题: throw new SyntaxError('JSON.parse'); 查询资料,大概意思如下: JSON ...
- JS面相对象
一.理解对象: //第一种:基于Object对象 var person = new Object(); person.name = 'My Name'; person.age = ; person.g ...
随机推荐
- ()()()()x()=()()()()填1-9数字
/** Function: ()()()()x()=()()()() Developer: Date: */ #include "iostream" #include " ...
- 【BZOJ3237】【AHOI2013】连通图 [CDQ分治]
连通图 Time Limit: 20 Sec Memory Limit: 512 MB[Submit][Status][Discuss] Description Input Output Sampl ...
- Docker原理 -- namespace与CGroup
命名空间 PID(Process ID) 进程隔离 NET(Network) 管理网络隔离 IPC(InterProcess Communication) 管理跨进程通信的访问 MNT(Mount) ...
- KMP算法_模板_C++
这个博客讲得非常优秀,可惜它是Java版本的 http://blog.csdn.net/yutianzuijin/article/details/11954939/ a 为匹配串,b 为目标串 通俗讲 ...
- pyhton发送邮件
# import smtplib # from email.mime.text import MIMEText # _user = "你的qq邮箱" # _pwd = " ...
- 千万不要使用xfce和KDE版Manjaro Linux--之荒谬言论
Manjaro Linux 使用经验: ①千万不要使用xfce版,虽然性能上廉价,但是吃亏,调声音80%几率卡死调不了,托盘图标很容易不响应!关机的Beep声,分分钟吓死人!按照网上各种方法弄,下次开 ...
- selenium与360极速浏览器driver配置
1)下载浏览器对应的driver,浏览器版本与driver对应关系,网址:http://www.cnblogs.com/JHblogs/p/7699951.html:driver下载地址:http:/ ...
- rhel5.5 linux系统下安装Oracle 11g
一.配置环境变量1.我将环境变量配置写成了一个脚本,将这个脚本copy到一个新建的linux系统.(脚本是本人原创,前2篇文章里有,感兴趣的朋友可以去看看) 2.进入脚本所在的目录. 3.执行脚本,需 ...
- javascript 将字符串当函数执行
//语法 eval(str); //demo var t = "func"; eval(''+func+'()');
- PHP7中php.ini、php-fpm和www.conf的配置(转)
根据前文 <2015博客升级记(五):CentOS 7.1编译安装PHP7> 的 configure 编译参数设定,安装后的PHP7配置文件所在路径是 /usr/local/php7/et ...