1.对象
万物皆对象,对象是一个具体的对象。
对象是一组无序的相关属性和方法的集合。
属性:对象的特征(名词);
方法:对象的行为(动词)。
字符串
,布尔
,数值
也为对象,也是通过对象方法创建的,也可以去调用对象的原始方法toString. (是通过原型链进行调用的,null和undefined不能调用对象原始方法)
创建字符串
var str = ‘xiaoming’;
str.toString();//因为调用时,js会自动进行装箱操作 String(‘str’) toString() 再进行拆箱 str
2.创建对象
2.1 字面量方式创建对象
var obj={ }; //创建了一个空对象
var obj={
属性名:属性值, //属性
方法名:匿名函数, //方法
...
}
创建的对象的属性和方法采用键值对
的方式,多个属性和方法之间用逗号隔开
。
var stu ={
name:'xiaoming', //属性
weigth:60,
heigth:180,
cook(){ //方法
console.log(this.name+'去做饭')
},
housework(){
console.log('去做家务')
},
}
2.2 使用new 创建对象
var obj=new Object( ); //创建一个空对象
obj.name='xiaoming'; //向空对象中添加属性
obj.age=19;
obj.sex='男';
obj.sayHi =function(){ //方法
console.log('hi');
}
利用等号赋值,添加对象的属性和方法
属性方法之间用分号隔开,以分号结束。
2.3工厂模式
function Dog(name,age){
var obj=new Object();
obj.name=name;
obj.age=age;
return obj;
}
var first=Dog('xioabai',1);
var second=Dog('xiaohei',2);
console.log(first);
console.log(second);
2.4构造函数方式创建对象
function Animal(name,age){
this.name=name;
this.age=age;
/* this.sayName=function(){
console.log(this.name);
}
每一次创建对象时都会开辟一个新的内存空间去存放方法*/
this.sayName=sayName;
}
//可以把函数写在全局,可以避免重复创建函数的尴尬
//但是这么使用封装性较差
function sayName(){
console.log(this.name);
}
var dog1=new Animal('xiaobai',2);
var dog2=new Animal('xiaohei',1);
console.log(dog1);
console.log(dog2);
dog2.sayName();
2.5原型模式
function Dog() {}
//在prototype中定义的属性和方法,所有的实例对象都共享
Dog.prototype.name='xiaobai';
Dog.prototype.age=2;
Dog.prototype.text={ //在原型模式创建时,如果属性是引用数据类型,可能会
t:'is dog'
};
Dog.prototype.sayName=function(){
console.log(this.name);
}
var dog1=new Dog();
dog1.sayName();
dog1.text .t ='is write dog';
dog1.name='zhangsan';
console.log(dog1);
2.6组合模式
在ES5中比较完美的一种创建对象的方式
function Dog(name,age){
this.name=name;
this.age=age;
this.arr=[];//这里是重复创建的,不会出现影响
}
Dog.prototype.sayName=function(){
console.log(this.name);
}
var dog1=new Dog('xiaobai',1);
var dog2=new Dog('xiaohei',2);
3.使用对象
3.1 调用对象的属性:
- 对象名 . 属性名
- 对象名[’ 属性名 ']
3.2 调用对象的方法:
- 对象名 . 方法名()
console.log(stu.name);
console.log(stu[‘name’]);
stu.cook();
3.3删除对象的属性
只能删除自定义属性,无法删除继承属性
delete stu.weigth;
3.4新增属性并赋值
若原对象没有改属性,则为新增,若有该属性,则修改值
stu.weigth=‘80’;
3.5遍历对象属性 for…in
for…in通常用来遍历对象属性。
能被for…in语句打印的属性称为可枚举属性,默认情况下自定义属性都是可以被枚举的。
for(var k in stu){
console.log(k); //对象属性的键(属性名)
}
for(var k in stu){
console.log(stu[k]); //对象属性的值
}
3.6变量,属性,函数,方法的区别
- 变量和属性
同:都是用来存储数据的;
异:变量需要单独声明并赋值,使用时直接写变量名,单独存在;属性在对象中不需要声明,使用时必须为 对象名.属性,依赖于对象。 - 函数和方法
同:都是实现某种功能;
异:函数时单独声明,并且调用时 函数名(),单独存在;方法是在对象里的,调用时需要 对象名.方法名(),依赖于对象。
4.构造函数
因为我们每次创建不同对象中的很多属性和方法大多是相同的,我们可以利用函数的方法重复这些相同的代码,把这个函数称为构造函数。
构造函数和普通函数不同,里面封装的不是普通代码,而是对象。
构造函数就是把对象里的一些相同的属性和方法抽象出来封装到函数里。
构造函数里的属性和方法
都是构造函数的成员
。
4.1创建构造函数
function 构造函数名(){
this . 属性 = 值;
this . 方法 = function( ) { }
}
new 构造函数名();
构造函数名首字母需要大写。
构造函数不需要return就可以返回结果。
调用构造函数必须使用new
。
只要调用函数就创建了一个对象。
属性和方法前必须添加this
。
//构造函数(泛指某一类事物)
function Star( name,age,sex ){
this.name=name;
this.age=age; //在构造函数内部通过this添加的成员为实例成员
this.sing = function( sang ){
console.log(sang);
}
}
Star.sex="男”; //在构造函数上添加的成员,为静态成员
//对象(具体的某个事物)
var ldh = new Star(‘刘德华’ ,18,‘男’); //调用函数,返回的是一个对象
var zxy = new Star('张学友' , 19 , '男') ;
console.log(ldh.name); //实例成员必须通过实例对象调用,不能通过构造函数调用
console.log(ldh[ 'sex' ]);
cinsole.log(Start.sex); //静态成员只能通过构造函数访问
ldh.sing('冰雨');
使用构造函数创建对象的过程也称对象实例化
。
成员分为实例成员和静态成员。
实例成员就是构造函数内部通过this添加的成员,实例成员只能通过实例化的对象来访问。
静态成员是在构造函数本身上添加的成员,静态成员只能通过构造函数访问。
4.2 new关键字
- 遇到new 构造函数在内存中创建一个空的对象;
- this指向创建的空对象;
- 执行构造函数中的代码,给空对象添加属性和方法;
- 返回这个对象。
new构造函数的过程
【1】创建了一个新的空对象 {}
var person = {};
【2】将新对象的 proto 指向构造函数的 prototype 属性
也就是设置这个对象原型指向构造函数
person.proto = Person.prototype;
【3】将构造函数的作用域赋值给新对象(this指向新创建的对象实例person)
这里注意Person.call(person, args)会为这个新对象person添加属性(同时this会指向新创建的对象)
另外返回值res为构造函数的返回值(因为这个过程相当于调用构造函数~)
var res = Person.call(person, ‘bill’, 21);
【4】返回新对象(这里注意 如果构造函数中return一个对象 那么会返回return的内容 而不是创建的这个对象!!)
return res;
截至此处 new关键字的工作就完成了
原型
构造函数存在浪费内存的问题,每创建一个实例对象,里面的实例方法就需要单独开辟一个内存空间存放。
构造函数通过原型分配的方法是所有对象共享的。
每一个构造函数都有一个prototype对象。
原型prototype是一个对象,这个对象的所有属性和方法,会被构造函数拥有。
将不变的方法定义在prototype对象上,所有对象的实例就可以共享这些方法,不需要为方法创建新的内存空间。
一般情况下,公共属性定义到构造函数内,公共的方法放到构造函数的原型对象上。
function Star( name,age ){
this.name=name;
this.age=age; //公共属性添加在构造函数
}
Star.prototype.sing=function(){
console.log('我会唱歌’)
} //公共的方法放在原型对象上
var ldh = new Star(‘刘德华’ ,18)
对象原型__proto__
每一个对象都有__proto__属性,指向我们构造函数的原型对象prototype,所以对象可以使用构造函数prototype对象的属性和方法。
__proto__对象原型和原型对象prototype是等价的
(对象的__proto__指向构造函数的原型对象prototype)
console.log(ldh.__proto__===Star.prototype) //true 实例对象上有__proto__指向构造函数的原型对象
ldh.sing(); //ldh对象可以指向sing()方法
方法的查找规则: 首先看对象身上是否有该方法,有则执行对象上定义的该方法,若没有则对象身上有__proto__,指向构造函数原型对象prototype,看prototype上是否有该方法,有则执行。
构造函数constructor属性
构造函数的原型对象prototype和对象原型__proto__中有个constructor属性
(构造函数),指向创建该对象的构造函数
。
console.log(ldh.__proto__.constructor);
console.log(Star.prototype);
constructor主要用于记录该对象引用于哪个构造函数
,可以让原型对象重新指向原来的构造函数。
很多情况下我们需要手动使用constructor这个属性指向原来的构造函数。
function Star( name,age ){
this.name=name;
this.age=age; //公共属性添加在构造函数
}
Star.prorotype={
constructor:Star;
sing:function(){
console.log('我会唱歌’)
},
move:function(){
consoe.log("我会演戏")
}
} //原型上有多个方法时可以将方法放在对象中
点(.)是指向构造函数原型中添加方法,而等号(=)是将prototype对象赋值为方法对象,这就导致构造函数里的prototype对象的值发生改变,原来的值被方法合集的对象所覆盖,prototype中没有constructor属性。
手动将constructor添加进prototype对象的属性中。
构造函数,实例,原型对象三者之间的关系
构造函数通过.prototype指向构造函数的原型对象,原型对象通过.constructor指向构造函数,构造函数创建实例对象,实例对象通过.__proto__指向构造函数的原型对象,通过. __ proto __.constructor指向构造函数。
原型链
实例对象的__proto__属性指向创建该实例对象的构造函数的原型对象,构造函数原型对象的__proto__属性指向Object原型对象prototype,该对象的__proto__指向null,构成了原型链,便于查找对象成员,一级一级向上查找原型对象里的成员,直到null。
成员的选择遵循就近原则。
当一个对象要调用一个自身不存在的属性或方法时,就从该对象的__proto__属性指向的prototype原型对象上找,没有找到则从该prototype原型对象的__proto__指向的prototype原型对象上找,依次类推,一级一级向上找,直至找到所需的属性或方法,或者得到null,就形成了原型链。
JavaScript成员查找机制(规则)
- 当访问一个对象的属性或方法时,首先查找这个对象自身有没有属性;
- 如果没有就查找他的原型(__proto__指向的prototype原型对象);
- 如果还没有就查找原型对象的原型(Object的原型对象);
- 以此类推一直到找到Object为止(null);
__proto__对象原型的意义在于为对象成员查找机制提供一个方向。
原型对象的this指向
在构造函数中this
指向的是对象实例
,具体指向的对象要等被调用的时候才能确定。
在原型对象中的函数里的this
指向的也是调用该函数的实例对象
。
function Star( name,age ){
this.name=name;
this.age=age; //公共属性添加在构造函数
}
var that;
Star.prototype.sing=function(){
console.log('我会唱歌’);
that=this;
} //公共的方法放在原型对象上
var ldh = new Star(‘刘德华’ ,18)
console.log(that===ldh); //true 输出原型对象函数中的this,指向调用他的实例对象ldh
对象类型转换
var obj ={
}
console.log(Boolean(obj)); //对象转为布尔类型时,当对象为空,则转为false,其他情况为true
console.log(obj.toString()); //对象转换为字符串
console.log(String(obj)); //对象转换为字符串
重写一个对象的toString
var obj={
name:‘xiaoming’,
toString:function(){
return ‘is’+this.name
}
}
console.log(obj.toString()); // isxiaoming
console.log(String(obj)); //重写tostring()时,使用String包装器时,先调用tostring()中的值,再进行包装
当基本数据类型调用原型方法时,实际JS完成了装箱(转换成对应类型的对象)和拆箱(把对象还原为原来的数据类型)的过程
var num1=123; //数值类型
var num2=Number(123); //数值类型
var num3=new Number(123); //对象类型
console.log(num1);
console.log(num2);
console.log(num3);
var str1=String(‘xiaobai’);
var str2=new String(‘xiaohei’);
console.log(str1);
console.log(str2);
function Animal(name){
return name;
}
var dog1=Animal(‘xiaobai’);
var dog2=new Animal(‘xiaohei’);
console.log(dog1);
console.log(dog2);
类型
console.log(typeof num2 ) //number
console.log(typeof num3) //object
console.log(num3.valueOf()= =num1);
console.log(num3===num2);
valueOf和toString的区别
1.这两个方法都是对象的原始方法
2.valueOf为对象的原始值,通常不会显示的调用,通常由js自动在后台进行调用
3.toString本身的一个作用是字符串的转换,也会进行自动转换
4.若重写了这两个方法,运算
时,优先调用valueOf
,在进行显示
时,优先调用toString
5.若只重写了一个方法,在运算和显示时,都优先调用该方法
var obj={
num:1,
toString:function(){
return this.num+1;
},
valueOf:function(){
return this.num+2;
}
}
//因为num在valueOf中被引用,所以不会被垃圾回收机制回收
console.log(obj==2);//3 运算时应该调用valueOf()
alert(obj);//2 浏览器弹窗
var obj={
num:1,
toString:function(){
return this.num+1;
},
valueOf:function(){
return this.num++;
}
}
console.log(obj==1); //比较运算调用valueOf() 第一次调用 1++
console.log(obj==2); // 每次调用obj时,都执行一遍valueOf() 2++
console.log(obj==3); // 3++
如果没有重写valueOf,那么转为数值类型时返回NaN,如果重写则优先调用valueOf
console.log(Number(obj)); //4
在对象中的检测属性和方法
in 关键词 检测某个属性是否是某个属性的自有属性或继承属性(原型属性)(可以使用的属性)
**hasOwnProperty(propertyName)**检测某个属性是否是某个对象的自有属性,不能是继承的属性
propertyIsEnumerable() 检测某个属性是否是某个对象的自有属性且可以枚举的属性
var obj={
name:'xiaoming'
}
console.log('toString' in obj); //true toString为对象的原始属性
console.log('name' in obj); // true name 为obj的属性
console.log('aaa' in obj); // false
console.log(obj.hasOwnProperty('toString')); //false toString为继承属性
console.log(obj.hasOwnProperty('name')); //true name为继承属性
console.log(obj.propertyIsEnumerable('toString')); //false 不是可枚举属性
检测是否在同一个原型链中(家族)
顶层对象包括属性和方法
isPrototyprOf测试一个对象是否存在于另一个对象的原型链上(原型的指向)
instanceof 检测一个对象是否是这个构造函数的实例 (对象是否由某个对象函数创建)
function Animal () {}
var dog1 =new Animal();
//instanceof 检测一个对象是否是这个构造函数的实例 (对象是否由某个对象函数创建)
console.log(dog1 instanceof Animal); //true dog1为构造函数Animal的一个实例
console.log(dog1 instanceof Object); //true 所有对象(自定义或继承)的顶层都为object
//isPrototypeOf测试一个对象是否存在于另一个对象的原型链上(原型的指向)
console.log(Animal.prototype.isPrototypeOf(d1)); //true 检测 d1是否在Animal的原型链上
var obj={};
console.log(Object.prototype.isPrototypeOf(obj));
function Dog(){}
Dog.prototype=new Animal();//原型链继承(将子类的构造函数的原型属性指向父类的实例)
var d2=new Dog(); //d2同时拥有Animal,Dog和Object的方法和属性
console.log(Animal.prototype.isPrototypeOf(d2));
设置一个对象的原始属性
**Object.defineProperty()**设置某个属性为原始属性
var obj={
name:'xiaoming',
age:12,
sex:'男'
}
Object.defineProperty(obj,'name',{ //修改name属性的原始属性
configurable:false, //不能被删除
enumerable:false, //不可枚举
writable:false, //不能修改
value:'lisi' //值改为'lisi'
})
console.log(obj) //不能枚举 {age:12,sex:'男'}
console.log(obj.name) //可以调用 lisi
obj.name='xiaoming'; //设置name原始属性不可修改,修改失败
delete obj.name; //设置为不可删除属性,删除失败
console.log(obj.name); //lisi
for(var k in obj){
console.log(obj[k]);
} //设置name为不可枚举 12 男
Object.getOwnProperityDescriptor() 显示某个属性的原始属性
var res=Object.getOwnPropertyDescriptor(obj,'name');
console.log(res);
Object.definePropertys一次设置对象多个属性的原始属性
Object.definePropertys(obj,{
age:{
configurable:false
},
sex:{
configurable:false
}
})
构造器属性
在对象中,我们可能会设置一些比较奇怪的属性_num,这种属性我们称为构造器属性
这种属性一般不希望直接通过外部访问(obj._num)
我们希望自己去控制这个属性的访问逻辑, obj.num可以访问到,然后对它进行一些逻辑改变
构造器属性可以重写自定义属性的get和set方法
var obj={
_num:0
}
obj.num可以访问到,返回 数字:0
Object.defineProperty(obj,'num',{
//当我们没有设置这个属性时,默认是隐式调用,如果设置了,会调用你设置的方法
//重写自定义属性的set和get属性
set(num){
thsi._num=num
}, //在num的值被设置时调用
get(){
return '数字:'+this._num
}//在num的值被获取时,调用
})
console.log(obj.num);
obj.num=5;
console.log(obj.num);
序列化和反序列化
JSON.Stringify();//系列化 将对象序列化为Json字符串,只能序列化对象可枚举的自有属性
JSON.parse(); //反序列化 将json字符串转换为对象