一、原型对象的问题
1.执行顺序
//先创建原型对象 --- 可以使用
//改变了 -- 原型对象 -- 的指向
//先实例化---无法使用后创建的原型对象
2.访问规则
// 原型链: __proto__ 链接的一条线(链)
// 访问规则(原型对象共享): *** 顺着__proto__这条线往上一直找,直到找到位置***
//原型链没有这个成员 --- 得到undefind
console.log(p.abd);
**/就近原则/**// ,自己身上有就不去原型链上找,在父亲身上有就不去爷爷身上找
console.log(p.__proto__);
// 在对象对象内 手动添加constructor指回Person
constructor:Person
3.属性判断的方法
function Person(name, age) {
this.name = name;
this.age = age;
}
// 推荐使用,不影响多人开发
Person.prototype.eat = function () {
console.log(this.name);
}
var p = new Person('lh', 20)
3.1 in 关键字
// 语法:‘属性’ in 实例对象
// 用途:返回的时布尔值 判断这个属性是不是属于对象的 实例成员或者原型成员(true)
console.log('name' in p);
console.log('eat' in p);
console.log('eat2' in p);
3.2 .hasOwnProperty 方法
// 语法 :实例对象.hasOwnProperty('属性名')
// 用涂:返回的是努尔 判断这个属性是不是属于对象的实例成员
console.log(p.hasOwnProperty('name'));
console.log(p.hasOwnProperty('eat'));
console.log(p.hasOwnProperty('eat2'));
3.3 练习
// 判断这个属性是不是只属于对象的原型成员 或实例成员
// 1.锁定范围(in) 2.剔除成员(hasOwnProperty)
console.log('***判断 实例成员***');
console.log(('name') in p && p.hasOwnProperty('name'));
console.log(('eat') in p && p.hasOwnProperty('eat'));
console.log('***判断 原型成员***');
console.log(('name') in p && !p.hasOwnProperty('name'));
console.log(('eat') in p && !p.hasOwnProperty('eat'));
4.新的instanceof认知
实例对象 instanceof 构造函数
判断 构造函数的原型对象 在不在 实例对象的原型链上
5.面向对象的三大特效
5.1、封装
5.2、继承
5.3、多态
6、继承
6.1混入式继承
//原理:拷贝继承,将父类成员拷贝到子类中
//语法实现 :for in 循环遍历父类 常用赋值到子类
let obj = {
name: 'lh',
age: 20,
Pets: {
name: '小乖',
age: 20
}
}
let newObj = {}
// 把obj的值给到newObj --- 复制的是引用地址
for (key in obj) {
// newObj.name=obj[name]
// ...
// newObj.wife=obj.wife ...
// 引用类型复制的是引用地址
newObj[key] = obj[key]
console.log(key);
}
/弊端 :数据安全问题(引用类型一改全改)/
console.log(newObj);
newObj.Pets.age = 5
console.log(newObj);
console.log(obj);
5.2原型式继承
// 实现原型式继承(子类是实例对象可以使用父类的原型方法)
// 原理:将 *父类的原型对象* 放在 *子类实例对象* 的原型链上
// 实现:子类.原型对象 = 父类.原型对象
// 功能:子类继承了父类的原型成员
/ 弊端 原型链结构混乱,类型不清晰/
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.eat=function(){
console.log(this.name+'的宠物,乖乖');
}
function Student(name,age){
this.name=name;
this.age=age;
}
Student.prototype=Person.prototype
var s=new Student('lh',20)
s.eat()
5.2.1、原型链继承
Student.prototype=new Person();//没必要传
// 注意:修改完原型指向后,顺便将constructor 属性**指回子类**
Student.prototype.constructor=Student;
var s=new Student('lh',20)
s.eat()
console.log(s.constructor);
// 完善原型链结构图
// Object.prototype 作为实例对象,对应的原型对象和构造函数是?? null
console.log('-----完善原型链结构图-----');
console.log(Object.prototype.__proto__);// null
/弊端:只能基础原型成员,不能继承实例成员/
5.3、借用构造函数继承
//使用借用构造函数继承 实现 实例成员的继承(解决的原型链继承的问题)
//原理:父类构造函数的代码 在子类中运行一次
//实现:看下方代码
/弊端:只能继承实例对象 ,不能继承原型对象/
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.eat = function () {
console.log(this.name + '的宠物,乖乖');
}
function Student(name, age) {
// 函数: 代码复用,函数内的代码再次运行 -- 函数调用
// Person(name, age)//函数调用,里面的this默认指向window
// **重**:解决this指向问题
// 1.将父类构造函数添加在子类的实例成员中
this.myFun=Person;
// 2.使用子类的实例调用方法(对象.方法()----this指向对象)
this.myFun(name,age);
// 3.用完后删除它
delete this.myFun;
}
var s=new Student('lh',20)
console.log(s);
7、call方法与apply方法
// call方法和apply方法:将某个**方法借用**给某个对象使用 --- *借给*
// 语法 : 被借用的对象.方法.call(借用对象)
// 被借用的对象.方法.apply(借用对象)
//特点(共有)
//1.借用之后方法立刻执行
//2.**借用之后,函数内部的this 指向了 借给的那个对象**
7.1相同点
//将对象的某个方法借给另一个对象使用
7.2.不同点
7.2.1 call方法传参
//从第二个参数起,使用,号隔开,传递的参数就是传给函数的参数
7.2.2 apply方法传参
//将所有传递给函数的参数打---包成一个数组,作为apply的第二个参数
7.3案例
var obj = {
name: '欧阳',
age: 20,
eat() {
console.log(this.name + '吃toufu');
},
add(a, b) {
console.log(a + b);
}
}
var obj2 = {
name: '东方',
age: 25
}
obj.eat();
//方法借用:将obj的eat方法借给obj2使用(相同点)
obj.eat.call(obj2);
obj.eat.apply(obj2);
// 不同点:
// 1.call 方法传参 ,从第二个参数起,和函数传参一样(用,隔开),传递的参数就是传给函数的参数
obj.add.call(obj2, 10, 20);
// 2.apply 方法传参 ,将所有传递给函数的参数 ,打包成一个数组,作为apply的第二个参数
obj.add.apply(obj2, [10, 30])
7.4、使用call与apply优化借用构造函数继承
function Studend(name,age,gender){
// 使用call与apply优化
// Person.call(this,'lh',20)
Person.apply(this,['lh',20])
this.gender=gender;
}
8、组合继承
// 原理:原型链继承 + 借用构造函数继承
// 父类
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 原型对象
Person.prototype.eat=function(){
console.log(this.name+'不爱');
}
//子类
function Student(name,age,gender,score){
// 借用构造函数继承 -- 实例成员继承
Person.apply(this,[name,age,gender]);
this.score=score;
}
// 原型链继承 -- 原型成员继承
Student.prototype=new Person();
Student.prototype.constructor=Student;
9.原型链完整结构图
function Student(name,age){
this.name=name;
this.age=age;
}
var s=new Student();
// 将所有的函数(包括构造函数)看做是实例对象
function fn(){}
console.log(fn.__proto__);
console.log(Student.__proto__);
console.log(Object.__proto__);
// 结论:所有函数__proto__ 指向的都是空函数(某个函数的原型对象)
console.log('-----空函数(原型对象) 的 构造函数-----');
console.log(Student.__proto__.constructor ==Function);
console.log(Object.__proto__.constructor ==Function);
console.log(fn.__proto__.constructor ==Function);
// 结论:所有函数都是右 Function 实例化而来
// 2.将 匿名的空函数(function.prototype)看做实例对象
console.log('----空函数的__proto__ 指向 Object.prototype-----');
console.log(Function.prototype.__proto__ === Object.prototype);
// 结论:Function.prototype 是由Object实例化出来的
// 3. 将 Function看做一个对象
console.log('-----Function看做一个对象----');
console.log(Function);
console.log(Function.__proto__ ==Function.prototype);
// 结论:Function.__proto__ 指向的是 自己的 原型对象
// Function 也是右 Function 实例化的
总结:
1.proto 原型链 所有对象都可以共享 Object 的原型成员
2. 实例化 所有的函数都是由 Function 实例化出来的 所有对象 都是由 构造函数实例化出来的