DaZeng:JS面向对象编程

21 篇文章 1 订阅
18 篇文章 0 订阅


面向对象(OOP)基本介绍

面向对象特性:

  1. 封装,此特性可隐藏对象内部的实现细节,对外提供一致的访问接口
  2. 继承,简单的代码复用机制,使子类拥有父类的特性。
  3. 多态,以一致的方式使用不用的实现,实现接口不变性。
  4. 抽象。

JS中如何体现OOP

  1. 只有对象,没有类(ES5)对象继承对象,而不是类继承类
  2. 原型对象是基于原型语言的核心概念,原型对象是新对象的模板,它将自身的属性共享给新对象。
  3. 顶层对象为Object
  4. 对象的构成:
  • 属性:属性描述了对象的状态。
  • 方法:是对象具有可实施的动作。

对象的创建和使用

// 对象的创建,new Object()返回一个对象
        let obj1 = new Object()
        obj1.eat = function(){
            console.log(this.name+'oooooo');
        }
        let obj2 = {}

        let obj3 = obj1
        obj3.name = 'xxx'
        obj3.eat()
        
        {
            obj4 = obj3
        }

        console.log(obj4);//demo.html:25 {name: "xxx", eat: ƒ}

        // {
        //     var obj4 = obj3
        // }

        // console.log(obj4);//demo.html:25 {name: "xxx", eat: ƒ}

        // {
        //    let obj4 = obj3
        // }

        // console.log(obj4);//报错

在这里插入图片描述

构造函数

构造函数就是ES5中的原型对象

function Person(name,age){
   this.name = name
   this.age = age
   this.eat = function(){
       console.log(this.name+'吃吃吃');
   }
}

let p = new Person('大曾','18')
console.log(p);

在这里插入图片描述

原型对象

  1. 实例化对象,通过__proto__获取原型对象
  2. 构造函数,通过prototype获取原型对象
  3. 实例化对象的隐式原型等于构造函数的显式原型(指向同一个原型对象)
//构造函数就是ES5中的原型对象
function Person(name,age){
    this.name = name
    this.age = age
    this.eat = function(){
        return (this.name+'吃吃吃');
    }
}

let p = new Person('大曾','18')
console.log(p);//实例化对象
console.log(p.__proto__);//指向Pereson原型对象
console.log(p.__proto__.__proto__);//指向Object原型对象

// 实例化对象的隐式原型等于构造函数的显式原型(指向同一个原型对象)
console.log(p.__proto__==Person.prototype);//true
console.log(Person.prototype.__proto__ === Object.prototype);//true

//1.实例化对象,通过__proto__获取原型对象
//2.构造函数,通过prototype获取原型对象

在这里插入图片描述

实例化对象上动态添加属性会不会影响其他的实例化对象?

 let p1 = new Person('aaa','10')
 p1.sex = '男'
 let p2 = new Person('bbb','10')
 console.log(p1);
 console.log(p2);

在这里插入图片描述

实例化对象上动态添加属性====>不会影响其他的实例化对象

在原型对象上添加属性呢?以及原型链查找?

let p1 = new Person('aaa','10')
p1.__proto__.sex = '男'
let p2 = new Person('bbb','10')
console.log(p1);
console.log(p2);
console.log(p2.sex,p2.__proto__.sex,p2.eat(),p2);

在这里插入图片描述
在这里插入图片描述

在原型对象上添加属性会影响,因为两个实例化对象都指向同一个原型对象。(涉及原型链)
p2先看自己自身有没有属性方法,没有就沿着原型链找。

在Person增加属性呢?

 //构造函数也是一个对象
Person.height = 100;
let p = new Person('大曾','18')
console.log(Person,Person.height,p);

在这里插入图片描述

构造函数也是一个对象,所以不影响其他的实例化对象。在放蜀山,扩展的属性,只有函数对象可以调用,其他的实例化对象是不能调用这个属性或方法。
console.log(Person);//函数还是函数
console.log(Person.height);//把函数当做对象来使用

怎样让实例化对象有新的方法?

//1.添加在自身
 p2.say=function(){
    console.log(this.name,'说加个关注吧');
 }
//2.添加在原型对象
 p2.__proto__.say=function(){
     console.log(this.name,'说加个关注吧');
 }
//3.构造函数原型对象添加
 Person.prototype.say=function(){
     console.log(this.name,'说加个关注吧');
 }
//4.添加在原型对象的原型对象上
 p2.__proto__.__proto__.say=function(){
     console.log(this.name,'说加个关注吧');
 }
//5.直接加载顶层Object中
 Object.prototype.say=function(){
     console.log(this.name,'说加个关注吧');
 }

 p2.say()

属性方法的删除

p2.say=function(){
   console.log(this.name,'说加个关注吧');
}

p2.new = 'new'
console.log(p2);

在这里插入图片描述
执行删除:

let p1 = new Person('aaa','10')
p1.__proto__.sex = '男'
let p2 = new Person('bbb','10')

delete p2.new //删除自身添加的属性
delete p2.say//删除自身添加的方法
delete p2.sex//删除原型链属性==》失败
console.log(p2);

在这里插入图片描述

delete p2.__proto__.sex//删除原型链属性==》成功(p1的sex也没有了)
delete p2.age //删除p2的age方法
console.log(p2);
console.log(p1);

在这里插入图片描述

对象的销毁

let p3 = p2
console.log(p3);//{name: "bbb", eat: ƒ}
p2 = null
console.log(p2);//null
console.log(p3);//{name: "bbb", eat: ƒ}

在这里插入图片描述

instanceof

instanceof : 返回一个对象 是否是通过另一个原型对象实例化出来的

function Student(){
   console.log();
}
let stu = new Student()
let arr1 = new Array()
let arr2 = []

console.log(p1 instanceof Person); //true
console.log(p3 instanceof Person); //true
console.log(p2 instanceof Object); //true

console.log(stu instanceof Student); //true
console.log(stu instanceof Person); //false
console.log(stu instanceof Object); //true

console.log(arr1 instanceof Object); //true
console.log(arr1 instanceof Array); //true
console.log(arr2 instanceof Object); //true
console.log(arr2 instanceof Array); //true

hasOwnProperty()

hasOwnProperty() 判断一个属性是否是对象自己的属性

console.log(p2.hasOwnProperty('sex'));//false
console.log(p2.hasOwnProperty('say'));//true
console.log(p2.hasOwnProperty('new'));//true

for in

for in 会遍历对象自己的属性和它原型链上的所有属性

for(let key in p2){
   console.log(key,'===>',p2[key]);
}

在这里插入图片描述

对象的get和set方法

调用对象分别输出1,2,3

//方法一
let obj = {
    a:1,
    get(){return this.a+=1},
    set(val){this.a = val}
}

console.log(obj.a);

for(let i = 0;i<2;i++){
    obj.set(obj.get())
    console.log(obj.a);
}

在这里插入图片描述

//方法二
let obj = {
 x:10,
  get a(){console.log('get'); return this.x+=1},
  set a(v){console.log('set',v); this.x = v}
}

obj.a = 0//调用set() v = 0
console.log(obj.a);//调用get()
console.log(obj.a);
console.log(obj.a);

console.log('x',obj.x);

在这里插入图片描述

//另一种写法,注意和上一种方法的区分
let obj = {
 get a(){console.log('get'); return a+=1},
 set a(v){console.log('set',v); a=v}
}

obj.a = 0
console.log(obj.a);//调用get
console.log(obj.a);
console.log(obj.a);

console.log('a',obj.a);

在这里插入图片描述

函数中变量的作用域

let a = 10
function test(){
    console.log(a);
}
test()//10


function test(){
    console.log(a);
    let a = 10
}
test()//报错


function test(){
    console.log(a);
    var a = 10
}
test()//undefined

function test(){
    console.log(a);
    a = 10
}
test()//报错

function test(){
    a = 10
    console.log(a)
}
test()//10
console.log(a);//10

------------------------------------------------------------
var a = 10; 
function test1(){
    console.log(a); //如果函数内部,找不到变量,就向函数的上一层作用域空间去找;
    var b = 20;
    console.log(b);  //内部变量的作用域,就只在声明的环境空间。
    c = 30;  //一个变量,如果没有使用var,let,const关键字来声明它,这个变量就会被声明成一个全局的变量。因此它的作用域就是全局环境空间。
    console.log(c);
    function test2(){
        var d = "hello";
    }
    test2()
    // console.log(d);//报错
}

test1();
// console.log(b);  //b is not defined, 在函数外层,是不能调用函数内部声明的变量的。
console.log(c);  // 30

var,let,const作用域比较

var

 var i = 1;
 for(; i< 10; i++){
     setTimeout(function(){
         console.log("i=",i);  // i = 10, 共9次;
     },10);
 }
 console.log(i);  // 10

let

//let 声明 的变量,只在声明的环境中,或者是 {}里有效;
for(let i = 1; i< 10; i++){
    setTimeout(function(){
        console.log("i=",i);  // i = 1,i=2,....,i=9; 
    },0);
}
console.log(i) // i is not defined

const

function test3() {
    const SEX = "男"
    if (true) {
        console.log("aaaa");//aaa
    }
    else {
        const PI = 3.14;
        console.log(PI);
    }

    console.log(SEX);  //男
    // console.log(PI);//PI is not defined

}

test3();
console.log(SEX);  // SEX is not defined

封装

为什么要封装?

隐藏内部的实现,只暴露出少量的接口供使用;

示例:

function myAjax (obj){
    console.log(obj);
}

myAjax = ""//目标被全局污染
myAjax();  // myAjax is not a function

在这里插入图片描述

目标被全局污染了(被篡改)

解决方法

改变函数的作用域

方法一:

function test(obj) {
    function myAjax(obj) {
        console.log(obj);
    }
    return myAjax;
}
let _ajax =  test()
_ajax(123)//123
myAjax = 2342;
myAjax()  //demo1.html:27 Uncaught ReferenceError: 

在这里插入图片描述
方法二:

函数表达式赋值

const fn1 = function(){
    console.log('执行');
}
fn1() //执行

在这里插入图片描述
方法三(推荐)

使用一个匿名函数,再使用函数自执行的方法,把函数包起来,利用函数内部变量、内部 的函数,只能在函数内部使用这个特性,来实现一个封装避免目标变量被全局污染。

//函数自执行之前,必须要有一个";"
(function(){
 function my1(){
     console.log(111);
 }
 function my2(){
     console.log(222);
 }
 function my3(){
     console.log(333);
 }
 // my()//111 公开接口
 window._my1 = my1
 window._my2 = my2
 window._my3 = my3
})();

_my1()
_my2()
_my3()

在这里插入图片描述

闭包

闭包是什么?

函数内部的变量,是不能在函数外使用的!
如果让函数内部的变量,可以在函数外面使用呢?解决方法就是使用闭包这个技术 。
闭包就是让函数内部的局部变量,一直被释放或销毁,一直存储于内存中。

闭包要注意什么?

不要直接返回闭包中的值;这样让外面的变量,直接引用此值,如果对外面的变量进行不合规则的操作,就会导致内部要隐藏的变量中数据错误!!
当闭包中,使用的是引用类型数据时,如数组时,应当避免直接把闭包的变量返回给用户。
一般的做法,就是重新声明一个引用类型的变量;只接收闭包变量中的数据,而不是通过赋值来直接引用闭包变量;
当要使用闭包的变量,是一个引用类型时,一定要注意,防止修改到函数内部的这个变量.

实现闭包的方式1

window.xxx = aaa;
暴露一个API;就可使用函数内部的方法了,同时这个方法,又要使用test1中的局部 变量,导致age变量,一直存储于内存中。
这样就是一个闭包的实现 。

function test1() {
let age = 30;

function getAge() {
    return age;
}

function setAge(value) {
    age = value;
}


window.get = getAge;
window.set = setAge;
}
test1();
set(1000);
let age = get();
console.log("age=", age);//1000

实现闭包的方式2

return xxx
不使用暴露的方法,使用return ,返回变量,对象,函数,数组和基本数据类型的字面量;

function test(){
    let uname = "xiao黄ren"
    function getName (){
        return uname;
    }
    function setName (v){
        uname = v;
    }
    return {getName, setName}
}
// 全局的变量,引用了函数内部的东西:如function,object,array; 
let obj = test()
obj.setName("小黄")
let n = obj.getName();
console.log(n); //小黄

实现闭包的方式3

类似于bind()(),在函数中,返回函数内部 的变量、对象、函数,都会形成一个闭包

function test1(){
            function test2(){
                console.log("test2");
                function test3(){
                    console.log("test3");
                    function test4(){
                        console.log("test4");
                    }
                    return test4;
                }
                return test3;
            }
            return test2;
       }
  // 函数的连续调用语法:
       test1()()()();
       
 // 分步理解:
    // let test2 = test1();
    // let test3 = test2();
    // let test4 = test3();
    // test4();

闭包的问题

由于长驻内存,导致内存占用过多,机器变慢;
IE中,会导致内存泄露的这个BUG.

在不需要使用闭包中的对象时,一定手动释放对象
obj = null;
只要不再引用闭包中的变量,对象,那系统就通过垃圾回收机制,自动销毁它们。

关于函数的声明和调用

普通的函数声明 ,可以在声明之前调用函数,也可以声明之后调用函数
因为: 函数声明 ,就和变量的声明一样,都会前置声明;
声明函数,不会执行函数体中的代码;调用函数时,才被执行

var test;
function test(){console.log(1)}
test()//1

函数表达式,只有代码执行到此处时,才进行赋值。
如果写的是函数表达式,则只能在表达式之后调用函数。

test()//test is not a function
test = function(){
   console.log(2)
}
test()//2

总之,函数应该坚持 先声明,再调用的书写习惯!!
变量也是,应该先正确的声明 ,再使用变量.

构造函数的继承及多态

call,apply,bind

把当前的实例化对象,传给父类中的this
语法:父类.call(子类的实例化对象,参数1,参数2,参数3,…)
注意,要先写继承的call(),apply(),再在它们后面写子类自己的属性。、

在子类中重新定义一个方法,实现一个多态的技术的表现;
同一个接口,不同的实现,就是多态。

function Person(name,age){
   console.log('Person',this);
    this.name = name
    this.age = age
    //多态,同一接口不同实现
    this.eat = function(){
        console.log('吃饭1');
    }
}
function Student(name,age,sname,sage,sex){
    console.log('Student',this);
    Person.call(this,name,age) //this在Person中指向Student
    //     Person.apply(this,[name,age])
    // Person.bind(this,name,age)()
    this.sname = sname
    this.sage = sage
    this.sex = sex
    //多态,同一接口不同实现,覆盖call内的eat
    this.eat = function(){
        console.log('吃饭2');
    }
}
let s1 = new Student('老师','22','学生','8','男')
console.log(s1);
s1.eat()

Person.prototype.color = 'red'
let p = new Person('曾曾','20')
console.log(s1,p,Person.prototype);

在这里插入图片描述

call(),apply(),只继承了父类自己的属性,父类的原型链的属性是没有继承下的。
在这里插入图片描述

封装ajax

核心判断type类型,url拼接,data转换,以及原生ajax使用

// 封装一个自己的ajax函数
(function(){
    function myAjax(obj) {
        // 1. 语法检查:
        if (typeof obj !== "object")
            throw new Error("参数必须是一个对象");
    
        if (typeof obj.url !== "string")
            throw new Error("参数url必须是一个string");
    
        if (typeof obj.type !== "string")
            throw new Error("参数type必须是一个string");
        if (typeof obj.async !== "boolean")
            throw new Error("参数async必须是一个boolean");
    
        if (typeof obj.success !== "function")
            throw new Error("参数success必须是一个function");
    
        if (typeof obj.data !== "object" && typeof obj.data !== "string")
            throw new Error("参数data必须是一个 object  or string.");
    
        // 2 创建一个xhr对象
        let xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveXObject("Microsoft.XMLHTTP")
        }
    
    
        // 3. 判断obj.type,是GET,还是POST?
        // console.log(obj.type.toUpperCase()); //'GET'
        if (obj.type.toUpperCase() === 'GET') {
            // GET时,把参数拼接到url后;
            obj.url = obj.url + "?" + formatData(obj.data);
            xhr.open(obj.type, obj.url, obj.async);
            xhr.send();
    
        } else if (obj.type.toUpperCase() === 'POST') {
            xhr.open(obj.type, obj.url, obj.async);
            // post,增加设置请求头代码,参数放在send()中;
            xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
            xhr.send(formatData(obj.data));
        } else {
            throw new Error(" 请求的type,必须get,post,put,delete,option几种类型之一")
        }
    
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                let response = xhr.responseText;
                // 调用回调函数,把后端数据做参数传入success.
                obj.success(response)
            }
        }
    
    
        // 把请求的参数,转换成key=value这种格式的字符串。
        // 参数的序列化,把对象序列化成字符串;JAVA后台,只能接收字符串格式 的参数;
        function formatData(param) {
            let str = ""
            if(typeof param === "object"){
                // // {a:1,b:2,c:3}  ==> "a=1&b=2&c=3&";  ===> "a=1&b=2&c=3";
                // for(let key in param){
                //     str += `${key}=${param[key]}&`;
                // }
                // // str = str.replace(/&$/,"")  //正则来处理
                // str = str.substr(0, str.length-1)  // 截取子串三种也可以。
                // // console.log(str); //a=1&b=2&c=3

                //深层{a:1,b:2,c:3,d:[1,2,3,{name:'user',pass:111},5],e:5}===>a=1&b=2&c=3&d=[1&2&3&{name=user&pass=111}&5]&e=5
                let str = JSON.stringify(data)
                str = str.slice(1,str.length-1) 
                str = str.split(',').join('&').split(':').join('=').split('"').join('')
                console.log(str);
    
            }else if(typeof param === "string"){
                str = param.trim();
            }
            return str
        }
    }
    // 公开一个接口
    window._ajax = myAjax;
})();



使用myajax
let url = "http://127.0.0.1:8000/test";
let type = "GET";
let data = {a:1,b:2,c:3};
let async = true;
let success = (data)=>{
 console.log(data);
 data = JSON.parse(data);
 if(data.status === 200){
     alert("请求成功")
 }
}
// 调用ajax(); 
_ajax({
 url,
 type,
 data,
 async,
 success
})

手写call,apply,bind

核心:

let p = new Person('曾曾','20')
let s1 = new Student('老师','22','学生','8','男')
for(key in p){
    console.log(key);
    console.log(p.hasOwnProperty(key));
    if(p.hasOwnProperty(key)==true){
        s1[key] = p[key]
    }
}

mycall

在这里插入图片描述
在这里插入图片描述

Father.myCall = function(obj,...args){
    let father = new this(...args)
    console.log(father);
    for(i in father){
        if(father.hasOwnProperty(i)){
            obj[i] = father[i]
        }
    }
}

function Father(money,money2){
    console.log(money,money2);
    this.money = money
    this.money2 = money2
    this.add = function(){
        console.log('积蓄'+this.money+'积蓄加一'+this.money2);
    }
}

Father.prototype.sfmoney = '私房钱'

function Son(money,money2,name,age){
    Father.myCall(this,money,money2)
    this.name = name
    this.age = age
    this.over = function(){
        console.log(this.name+'在'+this.age+'交女朋友花完了');
    }
}

let baba = new Father('20w','30w')
let son = new Son('10块钱','20块钱','儿子','18岁')//调用mycall方法
console.log(baba);
console.log(son);
son.add()

myapply

在这里插入图片描述
在这里插入图片描述

Father.myApply = function(obj,...args){
    let father = new Father(...args[0])
    console.log(father);
    for(i in father){
        if(father.hasOwnProperty(i)){
            obj[i] = father[i]
        }
    }
}

function Father(money,money2){
    this.money = money
    this.money2 = money2
    this.add = function(){
        console.log('积蓄'+this.money+'积蓄加一'+this.money2);
    }
}

Father.prototype.sfmoney = '私房钱'

function Son(money,money2,name,age){
    Father.myApply(this,[money,money2])
    this.name = name
    this.age = age
    this.over = function(){
        console.log(this.name+'在'+this.age+'交女朋友花完了');
    }
}

let baba = new Father('20w','30w')
let son = new Son('10块钱','20块钱','儿子','18岁')//调用mycall方法
console.log(baba);
console.log(son);
son.add()

mybind

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Father.myBind = function(obj,...args){
    let father = new this(...args)
    console.log(father);
    for(i in father){
        if(father.hasOwnProperty(i)){
            obj[i] = father[i]
        }
    }
    return function(name,age){
        console.log('1111',name,age);
    }
}

function Father(money,money2){
    console.log(money,money2);
    this.money = money
    this.money2 = money2
    this.add = function(){
        console.log('积蓄'+this.money+'积蓄加一'+this.money2);
    }
}

Father.prototype.sfmoney = '私房钱'

function Son(money,money2,name,age){
    Father.myBind(this,money,money2)(name,age)
    this.name = name
    this.age = age
    this.over = function(){
        console.log(this.name+'在'+this.age+'交女朋友花完了');
    }
}

let baba = new Father('20w','30w')
let son = new Son('10块钱','20块钱','儿子','18岁')//调用mycall方法
console.log(baba);
console.log(son);
son.add()

ES5实现构造函数实现继承

es5构造函数实现继承的几种方式:

  1. call,apply,bind 更改this指向实现继承,缺点不能继承原型链上的属性
  2. 儿子.prototype = new 父亲(),缺点无法传给父类参数
  3. for in 遍历赋值 (包含原型链)
    if (子[key] == undefined) {
    子[key] = 父[key];
    }
  4. 用一个函数xxx包裹父类,父类.prototype = xxx.prototype;
    再对xxx赋值更改原型的内容,如xxx.prototype.study = “好好学习,天天向上。”,父类自然就有了 ,外面使用 window.x = xxx抛出,a.study自然就有了,new xxx()指向父类

修改构造函数子类的原型

第一种方法已经说过了就不再赘述了,说第二种方法,修改构造函数子类的原型,让其指向父类。

// 创建一个人的构造函数(原型对象)
        function Person(name, age) {
            this.name = name;
            this.age = age;
            this.eat = function () {
                console.log("用手抓东西来吃;");
            }
        }
        
        // 创建一个学生的构造 函数(原型对象)
        function Student( cNumber, cName, sex) {
           console.log(Student.prototype)//Person {name: "张三", age: 12, eat: ƒ}
         
            this.cName = cName;
            this.cNumber = cNumber;
            this.sex = sex;
            this.eat = function () {
                console.log("用筷子夹东西来吃;");
            }
        }

         // 在第一行,写代码
        //  prototype 只能指向一个实例化对象,修改了当前实例化对象的原型链;通过原型链来实现属性的继承!

         Student.prototype = new Person("张三",12)
         
        let s1 = new Student(1, "平果", '男');
        console.log(s1);
        console.log(s1.name)//"张三"

        let s2 = new Student(1,"糖果",'女')
        console.log(s2);
        console.log(s2.name)  //"张三"
  • prototype 只能指向一个实例化对象,修改了当前实例化对象的原型链;通过原型链来实现属性的继承!
  • 缺点:只是实现了属性的继承,但是不能正常的在实例化student(子类)时,对继承的属性进行赋值;
    在这里插入图片描述

for in 实现继承

所有属性方法全部继承,包含原型链。

 // 创建一个人的构造函数(原型对象)
        function Person(name, age) {
            this.name = name;
            this.age = age;
            this.eat = function () {
                console.log("用手抓东西来吃;");
            }
        }

        // 创建一个学生的构造 函数(原型对象)
        function Student(cNumber, cName, sex) {
            this.cName = cName;
            this.cNumber = cNumber;
            this.sex = sex;
            this.eat = function () {
                console.log("用筷子夹东西来吃;");
            }
        }

		Person.prototype.xx = '原型属性'
        let p1 = new Person("吴刚", 23);

        let s1 = new Student(224, 'WEB前端', '男');

        // 让s1从p1实现属性的继承:
        // 从p1继承属性,那么就遍历 p1;
        for (let key in p1) {
            //    console.log(s1[key])  //如果没些属性,则是undefined;
            //    如果s1没有某个属性,那就添加这个属性:
            // s1.key  => s1[key]  ==> 当变量做对象的动态属性名时,使用[]来获取获取属性的值;
            if (s1[key] == undefined) {
                s1[key] = p1[key];
                // s1.name = p1.name;
                // s1.age = p1.age
            }
        }

        console.log(s1);

在这里插入图片描述

封装的函数的原型对象继承内部的原型对象

要求:
1. 对Person进行隐藏; 使用util的函数画封装它;
2. 当调用util()时,返回一个 new Person()的实例wx对象;
3. 在util.prototype上扩展的属性,去实现在Person的原型对象上去扩展;
4. 我要把util.prototype也隐藏了,对外提供一个叫fn的方法来修改util.prototype

通过修改prototype的指向 ,来实现继承
修改Person.prototype的指向,让它指向 uitl.prototype;因为,我们是在 util.prototype上进行属性扩展;
而new Person()实例的原型链上,没有study属性;因此,想要拿 study属性,只能修改原型链;
把new Person()的原型对象,指向 util.prototype;
通过修改prototype的指向 ,来实现继承;

普通函数方式

function util() {
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.eat = function () {
            console.log("用手抓东西来吃;");
        }
    }
    
    Person.prototype = util.prototype;
    return new Person();
}
util.fn = function(){
	util.prototype.study = "好好学习,天天向上
}

let p1 = util()
util.fn()
console.log(p1.study,p1)

在这里插入图片描述

自执行方式

(function(){
 		function _util() {
                function Person() {
                    this.name = "张三";
                    this.age = 22;
                    this.eat = function () {
                        console.log("用手抓东西来吃;");
                    }
                }
                Person.prototype = util.prototype; 
                //  Person.prototype ---> util.prototype;
                
                //  new Person().__proto__ ---> util.prototype;

                return new Person();
            }
            
            _util.fn = function(){
				util.prototype.study = "好好学习,天天向上
			}
 		window.util = _util;
})()

let p1 = util()
util.fn()
console.log(p1.study,p1)

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Da Zeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值