js面向对象总结(学习笔记)
对象---一切属性和方法的加工厂
面向过程:按步骤一步一步亲历亲为,注重过程
面向对象:根据需求,将对象的属性和方法分离出来,最终调用属性或方法,实现需求,注重结果
一、创建对象的三种方式:
1.new的方式
var obj = new Object();
var arr = new Array();
var str = new String();
2.字面量方式
var person={
name:"小明",
age:"20",
eat:function(){
console.log("吃东西")
}
}
3.构造函数方式
var Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
console.log("您好!~")
}
}
//创建实例
var person = new Person("小白",20);
person.say();
二、面向对象的特性
封装、继承、多态、抽象
封装性---将对象的属性和方法封装起来,供实例调用
继承性---子对象可以继承父对象的属性或方法
多态性---子类的方法会覆盖父类的方法,即表现的是多态性
抽象性---数据抽象、过程抽象
三、原型
原型的作用:
1.解决数据共享,节省内存空间
2.继承
原型是什么:
1.构造函数中一个属性prototype,该属性是一个对象,是个标准属性,给程序员用的,叫原型对象
2.实例对象中有一个属性__proto__,该属性是一个对象,是个非标准属性,给浏览器用的,叫原型对象
function Person(name, age) {
this.name = name;
this.age = age;
this.say=function () {
console.log("您好");
};
}
//原型添加方法
Person.prototype.say = function () {
console.log("您好");
};
var p1 = new Person("小明", 20);
var p2 = new Person("小花", 30);
//构造函数的protype与实例对象的__proto__相等
p1.__proto__ === Person.prototype
//原型对象的构造器constructor即为该原型对象的构造函数
Person.prototype.constructor===Person
四、构造函数、原型对象、实例对象之间的关系
1.实例对象是通过构造函数创建出来的;
2.构造函数中的prototype属性即为原型对象;
3.实例对象中的__proto__指向构造函数的prototype
五、this的指向
构造函数和原型对象中this指向当前的实例对象
六、原型链
原型链指的是实例对象和原型对象之间的关系
因为实例对象的__proto__指向构造函数的prototype,则对象的__proto__指向的肯定是某个函数的prototype
七、继承
1.改变原型指向实现继承
function Person(name,age){
this.name=name;
this.age=age;
}
//原型添加方法
Person.prototype.eat=function(){
console.log("吃东西");
}
//学生的构造函数
function Student(sex){
this.sex=sex;
}
//学生的原型
Student.prototype.play=function(){
console.log("大家一起玩");
}
//通过改变原型指向实现继承方法
Student.prototype = new Person("小明",20);
//学生实例化
var stu = new Student("男");
总结:
-
如果实例对象中的属性或者方法和原型对象中的重复了,那么此时使用的是实例对象中,但是是不会修改原型对象中的属性的值;
-
实例对象访问属性的时候,先在该实例对象中查找,有则使用,没有就去原型中找,找到了则直接使用,如果没有找到,则是undefined;
-
继承后,原型的指向改变,实例对象中的__proto__的指向也会发生改变,这样从原型对象中的属性和方法就继承过来了
2.借用构造函数实现继承
通过原型指向会引起属性一致的问题,故借用构造函数进行继承
function Person(name,age) {
this.name=name;
this.age=age;
}
//原型添加方法
Person.prototype.eat=function () {
console.log("我要吃东西");
};
//学生的构造函数
function Student(name,age,sex) {
//借用构造函数,call方法是改变了this的指向
Person.call(this,name,age);
this.sex=sex;
}
//实例对象
var stu1=new Student("小明",20,"男");
var stu2=new Student("小花",30,"女");
console.log(stu1.name,stu1.age,stu1.sex);
console.log(stu2.name,stu2.age,stu2.sex);
stu1.eat();//出错
总结:
-
可以解决属性重复问题,但方法无法继承
3.组合继承
改变原型指向,并借用构造函数实现继承
function Person(name,age) {
this.name=name;
this.age=age;
}
//原型添加方法
Person.prototype.eat=function () {
console.log("我要吃东西");
};
//学生的构造函数
function Student(name,age,sex) {
//借用构造函数,call方法是改变了this的指向
Person.call(this,name,age);
this.sex=sex;
}
//改变原型指向
Student.prototype=new Person();
Student.prototype.run=function () {
console.log("来啊,我们一起奔跑啊");
};
//实例对象
var stu1=new Student("小明",20,"男");
var stu2=new Student("小花",30,"女");
console.log(stu1.name,stu1.age,stu1.sex);
console.log(stu2.name,stu2.age,stu2.sex);
stu1.eat();
stu1.run();
八、拷贝继承
分为浅拷贝和深拷贝
浅拷贝:复制对象
var obj1={
age:20,
cat:["大黄","加菲猫"]
};
var obj2=obj1;//浅拷贝继承
obj2.age=100;
深拷贝:找到一个对象的属性或方法,在另一个对象内重新开辟一个空间,存储到另一个对象中
var obj1={
age:10,
sex:"男",
car:["奔驰","宝马","特斯拉","奥拓"],
dog:{
name:"大黄",
age:5,
color:"黄色"
}
};
//空对象
var obj2={};
//a---当前的对象,b是一个目标对象,把a对象中所有的内容全部拷贝到b对象中
function extend(a,b) {
//遍历过程
for(var key in a){
//先获取a对象中的每个属性值
var item=a[key];
//判断这个属性的值是不是数组
if(item instanceof Array){
//如果数据是数组,那么就在b对象中添加一个新的属性,并且这个属性的值也是数组
b[key]=[];
//调用方法,把对象a中的数据一个一个的复制到b对象的这个数组中
extend(item,b[key]);
}else if(item instanceof Object){
//如果这个数据是对象,那么b对象中添加一个属性,也是对象类型的
b[key]={};
extend(item,b[key]);
}else{
//如果属性的值是普通的数据,那么就直接复制到b对象的属性中
b[key]=item;//直接赋值
}
}
}
//调用方法
extend(obj1,obj2);
obj2.age=100;
console.log(obj1.age);
console.log(obj2.age);
区别:
浅拷贝,第一个对象和第二个对象的地址是相同的,其中一个对象的属性值改变,另一个对象的属性值也会发生变化;
深拷贝:两个对象之间发生拷贝后,属性的值是不会相互影响的
九、对象冒充
//构造函数
function Animal(name,color) {
this.name=name;
this.color=color;
}
//通过原型添加方法
Animal.prototype.eat=function () {
console.log("今晚吃的是榴莲沾臭豆腐汁,还有啃了两头大蒜");
};
//构造函数
function Dog(name,color,age) {
this.age=age;
//对象冒充----仅仅是盗用了别人的构造函数实现属性的继承,并不能实现方法的继承
this.Animal=Animal;
this.Animal(name,color);
delete this.Animal;//删掉了
}
//实例化
var dog=new Dog("大黄","黄色",20);
console.log(dog.name,dog.age,dog.color);
dog.eat();
十、原型的不同写法
1.通过原型添加属性或方法
Person.prototype.name="乔峰";
Person.prototype.play=function () {
console.log("走,出去玩儿");
};
2.将属性和方法直接写到原型对象中
Person.prototype={
constructor:Person,//一定要把这个加上
name:"卡卡西",
age:20,
sex:"男",
eat:function () {
console.log("吃");
},
play:function () {
console.log("玩");
}
};
var p=new Person();
十一、动态原型
会让这种写法看起来更像是面向对象语言中的类的写法
function Person(name, age) {
this.name = name;
this.age = age;
//每次把原型的方法写在构造函数外部很麻烦,所以,可以这么写
//在构造函数初始化的时候直接添加原型方法(如果构造函数没有初始化,那么原型方法也不会添加(只要实例化对象那么就会有原型方法))
if (typeof Person.init == "undefined") {
//原型方法
Person.prototype.sayHi = function () {
console.log("Hi~~");
};
Person.init = true;//标识
}
}
var p = new Person("小白", 20);
p.sayHi();
十二、通过原型为系统对象添加方法
沙箱的形式:函数自调用,内部变量的变化不会影响函数外相同名称的变量的变化
(function(){
String.prototype.myTrim = function(){
return this.replace(/\s*/ig,"");
}
})();
var str=" 欢迎大家 来到我的博客 ";
console.log("==="+str.myTrim()+"===");

浙公网安备 33010602011771号