原型对象-原型链

264 阅读5分钟

一、原型对象的问题

1.执行顺序

//先创建原型对象 --- 可以使用 
//改变了  -- 原型对象 -- 的指向


//先实例化---无法使用后创建的原型对象

2.访问规则

原型对象原型链 .png

// 原型链:  __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()

原型式继承 .png

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.原型链完整结构图

原型链完整结构图.png

        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 实例化出来的 所有对象 都是由 构造函数实例化出来的