面向对象它是一个编程思路,不会改变我们预期的效果。
优点: 使用面向对象,会让代码的复用性大大的提高,便于迭代
创建对象方式
1. 字面量
let obj = { name:“张小涛”,nan,}
2.通过构造函数
let obj2 = new Object();
3.对象的使用
1.通过点语法console.log(obj.name); obj.hobby();
2… 通过中括号 console.log(obj[“nan”]);
上述两者的区别: 如果通过点语法无法获取到对象中“正确”的属性/方法名,可以使用中括号的方式来获取
原型
每次通过构造函数去实例化对象,都会生成一个新的内存空间,当项目开发中,
往往大量的内存使用,会严重影响性能
1.构造函数 function Person(name,sex){
this.name = name;
this.sex = sex;
this.hobby = function(){
console.log(“喜欢游泳~”); };
}
原型: 将公有的方法,放到原型中,每次实例化对象,原型中的内容是公有的,不会再分配新的空间给对象 (构造函数中的属性/方法 会)
原型 Person.prototype.s = function(){
console.log(this.sex); }实例化对象let san = new Person(“张三”,“男”); console.log(san.name);
工厂模式
function Person(name,age,sex,fn){
1. (形参) --> 运输材料
2. 创建对象并赋值 --> 加工材料
let obj = {};
obj.name = name;obj.age = age; obj.sex = sex;
obj.hobby = fn;
return obj; // 3. 出厂
}
构造函数继承
**继承:**通过“某一种方式”让一个对象,可以访问并使用到另一个对象中的内容。
这种行为我们叫做 继承!
继承的作用: 节省了各个构造函数中,大量重复声明的属性和方法。
提高了开发效率,并节省了内存
构造函数继承,只可以继承(借用) 构造函数中的内容
无法继承(借用) 原型中的内容
继承的方式一: call和apply方法的使用 ,达成了构造函数继承的作用
构造函数的继承:
优点 简约易使用
缺点 只可以继承(借用) 构造函数中的内容
无法继承(借用) 原型中的内容
function fn(){
console.log(this);
}
function Animal(name,sex){
this.name = name;
this.sex = sex;
this.jiao = function(){
console.log(this);
}
}
直接给原型赋值一个对象,可以简化批量赋值。 但,等于重构了原型
重构原型时,需要手动创建constructor: 构造函数。 否则原型的指向链条会断
Animal.prototype = {
constructor:Animal,
ll:“lala”,
fun:function(){
console.log(“我是原型中方法”);
}
};
call: 对象冒充, 又叫做对象借用
fn.call(obj,“大象”,“公”);
apply:作用和call 一致, 都借用对象。
区别:
call 接受参数时,数量根据被借者需要的参数而定
apply: 只有两个参数 1.借用者 2.数组(这里存放着所有的参数)
let arr = [name,age,sex,hobby];
Tiger.apply(this,arr);
工厂模式
创建对象的第三种方式: 工厂模式
function Person(name,age,sex,fn){
1. (形参) --> 运输材料
//2. 创建对象并赋值 --> 加工材料
let obj = {};
obj.name = name;
obj.age = age;
obj.sex = sex;
obj.hobby = fn;
return obj; // 3. 出厂
}
原型链继承
原型链继承: 将父类的实例化对象 赋值 给子类的原型
优点:
类似于复制了一个对象,构造函数和原型中所有的数据都可以获取到。简约方便使用
缺点:
1.原型链继承自身没有办法传参。
2.父类所有的属性和方法都可以被所有子类共享
组合继承: 类式继承,无法获取父类的原型链
原型链继承,无法直接给父类传参
组合继承 = 类式继承 + 原型链继承
结合了两种继承的优点,互相弥补了各自的缺点
1.定义一个构造函数 – 父类
function Parent(name,sex,age){
this.name = name; this.sex = sex; this.age = age;
this.hobby = function(){
console.log(“喜欢下象棋”);}
}
Parent.prototype.jida = function(){
console.log(“考不好,倒吊起来打”);}
子类
function Son(name,sex,age,score){
Parent.call(this,name,sex,age);
this.score = score;}
Son.prototype = Parent.prototype ;
let s1 = new Son();
s1.jida();
传值和传址
复杂数据类型–传址:
浅拷贝: 只拷贝数值,不拷贝地址。 节省内存。
但相互影响
let DadProto = { name :“张三”, age:20 } l
et SonProto = DadProto;
简单数据类型–传值:
let a = 10;
let b = a;
b = 20;
解决传址 相互影响的问题
深拷贝 : 数值和地址都拷贝。 内存独立,不相互影响
let DadProto = { name :“张三”,age:20,
text:undefined,
fn:function(){ // 这里会返回一个新的对象,达成深拷贝的效果 ,但是这种方式,会丢失function 和 undefined
console.log(“哈哈”);} }
解决方式一:
let SonProto = JSON.parse(JSON.stringify(DadProto));
解决方式二:
function deepCopy(obj){ //封装函数 let newObj =
Array.isArray(obj)?[]:{};
for-in: 会遍历对象和原型以及原型链上的属性和方法
但是我们做深拷贝的时候,不需要去拷贝原型链的内容
for(let key in obj){ //hasOwnProperty(): 用来检测指定的内容是否是自身,返回布尔值
if(obj.hasOwnProperty(key)){
//判断是否是对象(复杂数据类型)
if(typeof obj[key] === “object”){
//如果内部的数据,依然是个对象
//再调用自身,重复检测赋值一遍
newObj[key] = deepCopy(obj[key]);
}else{ //简单的数据类型
newObj[key] = obj[key]; }
}
}
//最后,将我们检测并赋值的新对象返回! 达成新内存的目的
return newObj; }
操作符
1. instanceof: 判断一个实例对象 是否是某个函数的实例
语法: 对象名 instanceof 函数名
2. isPrototypeof(): 只要是原型链中出现过的原型,都可以说是该原型链所产生的原型,返回true
语法: 原型名.isPrototypeOf(对象名)
简述:会从s1开始沿着原型链逐层向上找,看看有没有一个叫Son.prototype的
console.log(Son.prototype.isPrototypeOf(s1));
**3.hasOwnProperty()😗*用来检测当前对象中是否有该属性, 返回值为布尔类型
不会检查原型和原型链上的内容
只会去查找构造函数中的内容
语法: 对象名.hasOwnProperty(“属性/方法名”);
console.log(s1.hasOwnProperty(“fn”));
4.delete: 用来删除属性 delete
s1.name;console.log(s1);
5. in
检查该对象是否包含指定属性
这个属性可以是对象的直接属性
也可以是prototype继承而来的属性(
但是原型自身的属性/方法不行(原型链继承,重构原型,覆盖掉了)
语法: 属性/方法名 in 对象名 console.log(“a” in s1);