一、面向对象的概念
面向对象是一种编程思想
对象:可以添加属性(变量)和方法(函数);面向对象写法特点:
1、把功能写成一个函数;
2、函数必须写在对象身上,调用的时候直接拿对象.方法名();
3、对象身上的功能可以继承;
4、this指向,在事件或者定时器里面是访问不到真正的this的,需要在外面存一下;
写法示例:
function 构造函数 (){
this.属性=??;
}
构造函数.prototype.方法=??;
var 实例=new 构造函数();
实例.方法();
二、new的作用
一元运算符,后面只能接函数,用来调用函数;
用new调用函数的过程:
1、构建一个空对象; 2、把函数内部的this指向创建的对象; 3、在函数执行完以后自动返回刚才创建的那个对象,即使函数里面没有return;假如函数后面有return的话会有两种情况: A 、return后面是对象的话,则返回这个对象; B 、return后面是非对象的话,则返回一开始new创建的对象;
注意:
1、用new调用的函数永远返回一个对象,不管有没有返回值; 2、用new调用函数,这个函数一定是用来创建对象的,叫做构造函数;
三、构造函数
创建并初始化对象的函数,并且必须用new调用,不然和普通函数没有区别;
functon Person(name,age){
this.name=name;
this.age=age;
this.say=function{
console.log("my name is"+this.name);
};
};
new出来的都是构造函数的实例,构造函数就是实例化的过程;构造函数的问题:性能问题会造成资源浪费;
四、原型 prototype
概念:函数身上的属性,每个函数都有,它的值是一个对象;
用途:因为它是一个对象,所以身上可以放属性和方法,如果与构造函数相结合,通过构造函数创建的对象就会具有原型身上的属性和方法;
建议把一些共用的属性和方法放在构造函数的原型身上;
五、proto
对象身上的属性,每个对象都有这个属性;它的值也是一个对象,它的值就是它对应的构造函数的prototype的值;
实例.proto===构造函数.prototype;这句话解释了为什么实例能够继承构造函数身上的属性和方法;
functon Person(name,age){
this.name=name;
this.age=age;
};
this.prototype.say=function{
console.log("my name is"+this.name);
};
var p1=new Person("peter",18);
在这个例子中:p1.__proto__=Person.prototype;所以p1才能继承Person构造函数身上的属性和方法;
六、原型链
对象与原型之间的关系(链接)
原型链查找规则:
七、包装对象
-
在js中,当我们去调用字符串,布尔值,数字的属性和方法的时候,js会在内部把这些基本数据类型转变成一个对应的对象类型(包装对象),然后再调用这个包装对象的属性和方法;
-
包装对象有:
-
注意:
- null和undefined没有包装对象;
- 基本数据类型只能使用对应的包装对象身上的属性和方法;
八、hasOwnProperty
作用:判断一个属性是不是自己对象身上的;
语法:对象.hasOwnProperty(属性);
参数:要检测的属性;
返回值:
**true** *自身属性*
**false** *非自身属性*
注意:
1、这个方法是Object身上的方法 2、不会顺着原型链往外面去查找属性,只查找自身
九、constructor
概念: 每个对象身上都会有这个属性,默认指向该对象对应的构造函数;
这个属性不是放在对象身上,放在对应的原型对象身上;
作用:查看对象的构造函数;
语法:对象.constructor;
返回值:对象的构造函数;他可以用来做数据类型的判断;
constructor的问题 :constructor的值是可以修改的;
function Person(name){
this.name=name;
}
var p1=new Person('kaivon');
console.log(p1);
console.log(p1.constructor==Person); //true
p1.constructor=Array;
console.log(p1.constructor==Array); //true
十、 toString
作用:把对象类型转成字符串;
注意:系统对象身上的这个方法都是在对应的原型身上;而自己写的构造函数这个方法在达的Object原型身上
它可以用来做类型判断:
Object.prototype.toString.call( );
//用toString做类型判断
var num=0; //[object Number]
var str='kaivon'; //[object String]
var b=true; //[object Boolean]
var n=null; //[object Null]
var u=undefined; //[object Undefined]
var arr1=[]; //[object Array]
var obj1={}; //[object Object]
var fn=function(){}; //[object Function]
var d=new Date(); //[object Date]
var re=new RegExp(); //[object RegExp]
console.log(Object.prototype.toString.call(num));
十一、浅拷贝与深拷贝
基本数据类型复制:
var num1=123;
var num2=num1;//那么num2和num1就是一样的,**复制了值**
复杂数据类型复制的问题:
var arr1=[1,2,3];
var arr2=arr1;
arr2.push(4);
//注意这个时候arr1和arr2的值;
console.log(arr2); //[1,2,3,4]
console.log(arr1); //[1,2,3,4]
arr1和arr2一样的原因:复杂数据类型复制的时候不仅仅是复制了值,还复制了引用地址,所以修改arr2时,那么arr1的地址和arr2其实还是一样的,所以arr1的值也相应改变了;
思考:那么我们应该如何复制复杂数据类型?
浅拷贝;
var obj1={a:10,b:20}; //如何复制呢?
function copy(obj){
var newObj={};
for(var attr in obj){
newObj[attr]=obj[attr];
}
return newObj;
}
var obj2=copy(obj1);
//这时候的obj2和obj1看起来是一样的,但是其实是不同的,有不同的引用地址;
深拷贝
深拷贝和浅拷贝的区别:1、当要复制的那个对象里面所有的值都是非对象的时候,用浅拷贝;2、当要复制的那个对象里面有对象的时候,用深拷贝;
var obj1={
a:10,
b:{
c:20 //这时候对象里面还有对象;这时候怎么办呢?
}
}
//如果这时候还用上面的浅拷贝的话,那么修改复制后的值,原来对象里面的值也还是会改变,所以要用下面的办法;
function deepCopy(obj){
if(typeof obj!=="object"){
return obj;
}
//上面的代码是给了一个跳出条件,当传入的条件不是对象的时候就不需要递归了;
if(obj instanceof Array){
var newObj=[];
}else{
var newObj={};
}
for(var attr in obj){
newObj[attr]=deepCopy(obj[attr]);
//递归;当对象里面的值还是对象的时候需要用到;
}
return newObj;
}
十二、继承
概念:
让一个对象拥有另一个对象的属性和方法,并且自己添加的属性和方法不会影响原来的对象;
属性继承:
通过call的方法调用构造函数来继承属性;
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.say=function(){
console.log('我叫'+this.name);
}
//Person相当于一个大类,Coder相当于一个小类,他们是有共同的地方的;
function Coder(name,age,job){
this.name=name;
this.age=age;
this.job=job;
}
Coder.prototype.say=function(){
console.log('我叫'+this.name);
}
//通过属性继承,我们可以将小类和大类共同的部分简化如下:
function Coder(name,age,job){
Person.call(this,name,age);
//如果不用call的话,那么this的指向就是window了,所以要用call将指向指到当前的this上面;
this.job=job;
}
方法继承:
通过for in 想要继承的构造函数的原型的方法去去继承;
参考属性继承