面向对象01——对象的创建、工厂模式、new运算符、构造函数、原型prototype、构造函数继承、原型的继承、原型链、包装对象、面向对象和面向过程编程、类和对象概念

  • 对象的创建
  • 工厂模式
  • new运算符
  • 构造函数
  • 原型prototype
  • 构造函数继承
  • 原型的继承
  • 原型链
  • 包装对象
  • 面向对象和面向过程编程
  • 类和对象概念

面向对象编程思想(理解)主要在“程序设计思维”

一、面向过程:注重解决问题的步骤,分析问题需要的每一步,实现函数依次调用;

二、面向对象:是一种程序设计思想。将数据和处理数据的程序封装到对象中;

三、面向对象特性: 抽象、 继承、封装、多态

优点:提高代码的复用性及可维护性;

<script>
// 小明去餐厅吃饭:
// 面向过程:1.小明走去餐厅看菜单点餐吃饭;
// 面向对象:1.小明(走、看、点餐、吃); 2.餐厅(菜单);
// 研究对象间的关系:小明.走餐厅.菜单 小明.看  小明.点餐 小明.吃;
</script>

对象

Javascript 是一种基于对象的语言,几乎所有东西都是对象;
1、对象创建方法:

  • 字面量创建
  • new Object()创建
  • Object.create()创建:创建对象的原型;
<script>
// 对象创建
// 1.字面量方式
let str = 'name';
let obj = {
    [str]:"张三",//[str]可以是表达式构成
    age:"20",
    hobby:function(){
        console.log("喜欢篮球")
    }
}
// 2.构造函数;
let obj = new Object();
obj.name = "张三";
obj.age  = 20;
obj.hobby = function(){
    console.log("喜欢篮球")
}
console.log(obj);
// 3.Object.create() ;属性方法放在原型上;
let obj = Object.create({
    name:"张三",
    age:20,
    hobby(){
        console.log("喜欢篮球");
    }
 })
console.log(obj)

// 对象的调用;
console.log(obj.name);
obj.hobby();
console.log(obj['name']);

// 以上对象中.name和中括号调用的区别
let str = "name";
// console.log( obj.str);//错误
console.log( obj[str]);
</script>

工厂模式

一、工厂模式
工厂模式解决了代码复用的问题;

<script>
// let zhangsan = {
//     name:"张三",
//     age:20,
//     hobby(){
//         console.log("喜欢篮球");
//     }
// }
// let lisi = {
//     name:"李四",
//     age:21,
//     hobby(){
//         console.log("喜欢足球");
//     }
// }

// 以上的写法:冗余

// 解决冗余(提高代码复用性,冗余少):工厂模式(类:不是代表哪一个,是代表某一类)
function Person(name,age,hobby){
    let obj = {}; // 添加原料

	// 加工原料
    obj.name = name;
    obj.age = age;
    obj.hobby = function(){
        console.log(hobby);
    }
    
    return obj; //出厂;
}

let zhangsan  = Person("张三",20,"喜欢篮球");
let lisi  = Person("李四",21,"喜欢足球");
console.log(zhangsan);
console.log(lisi);

</script>

对象和类

​ 一、对象:具体的某个事物;(如:小明、叮当猫)

​ 二、类:一类事物的抽象;(如:人类、猫类)

new运算符

  • new的特点:
    1.new执行函数
    2.自动创建空对象;
    3.this绑定到空对象;
    4 隐式返还this;
  • 通过new来改造工厂模式
<script>
// let str = "";
// let str = new String();

// 1.执行函数;2.自动创建一个空对象; 3.把空对象和this绑定 4.如果没有返还,隐式返还this;
// function test(){
//     console.log("test");
// }

// 执行函数
// test();
// new test();
// new test;

// function Test(){
//     // let obj = {};  === this;

//     // return this;
// }

// new Test();


// 工厂模式;
function Person(name,age,hobby){
    // let obj = {}; // 添加原料  === this; 创建一个空对象

	// 把空对象和this绑定
    this.name = name;
    this.age = age;
    this.hobby = function(){
        console.log(hobby);
    }
    // 加工原料
    // return obj; //出厂;隐式返还this
}

let zhangsan =  new Person("张三",20,"篮球");
console.log( zhangsan.name);
zhangsan.hobby();

</script>

构造函数

  • 构造函数要通过new来调用this指向实例化对象
  • 约定俗成构造函数首字母大写
  • 静态属性及方法
    • 静态方法里的this;

类是泛指(抽象)
对象是特指

<script>
// 构造函数;1.首字母大写;2.this指向实例化对象;
function Person(name){
    // this.num = 0;
    this.name = name;
    this.age =20;
    this.hobby = function(){
        console.log("喜欢篮球");
    }
}
// 静态成员;
Person.num = 0;
Person.fn = function(){
    console.log("fn");
}

// new  : 实例化;
let zhangsan  = new Person("张三");
Person.num++;

let lisi  = new Person("李四");
Person.num++;

console.log(Person.num);// 静态属性和方法;(属于类本身的);

</script>

构造函数性能

  • 公共空间存放公共方法
<script>

function Person(name){
    this.name = name;
    this.age = 20;
    this.hobby = function(){
        console.log("喜欢篮球");
    }
}

let zhangsan = new Person("张三");
let lisi = new Person("李四");

console.log(zhangsan.hobby===lisi.hobby);//false
//在内存内分配了两个地址,张三一个,李四一个,(内存占用)
</script>

构造函数原型

prototype原型(解决了性能空间,内存问题)

  • 通过new实例化出来的对象其属性和行为来自两个部分,一部分来自构造函数,另一部分来自原型。
  • 当声明一个函数的时候,同时也声明了一个原型 。
  • 原型本身是一个对象。
  • 对象属性方法查找规则;
    在这里插入图片描述
<script>
function Person(name){
    this.name = name;
    this.age = 20;
    // this.hobby = function(){
    //     console.log("喜欢篮球");
    // }
}

// 功能空间原型;
 Person.prototype.hobby = function(){
     console.log("喜欢篮球");
 }
 Person.prototype.fn = function(){
     console.log("fn");
 }

// 不能这样写,已覆盖掉prototype ,无固有属性,需自己写上constructor
 Person.prototype = {
     constructor:Person,
     hobby:function(){
         console.log("hobby");
     }
 }
 
let zhangsan = new Person("张三");
let lisi = new Person("李四");

console.log(zhangsan.hobby===lisi.hobby);//true,这样存放在prototype空间内无需分配内存地址,同一地址-->true

// 实例化对象zhangsan的原型与Person的原型是同一个东西,只是表现形式有所不同
console.log(zhangsan.__proto__===Person.prototype);

// 原型的固有属性 === 构造函数
console.log( Person.prototype.constructor===Person);
console.log(zhangsan.constructor===Person);
// console.log(zhangsan);

// constructor作用:找到实例化对象是通过哪个构造函数来实例的(实例化对象的指向问题);通过constructor判断类型,判断指向

// 判断类型;
// let str = new String("abd");
// let str = "abc";
// console.log(str.constructor===String);true

</script>

原型构造函数及对象关系

<script>
    let temp;
function Person(name){
    this.name = name;
    this.age = 20;
    
}
Person.prototype.fn = function(){
    console.log("fn");
    temp = this;
} 
console.log( Person.prototype.constructor===Person);
let zhangsan = new Person("张三");
zhangsan.fn();
// console.log(zhangsan===temp)

// 构造函数Person内的this等于实例化对象
// 原型prototype的this等于实例化对象

</script>

工厂模式对比构造函数

1.但是却没有解决对象识别的问题。即创建的所有实例都是Object类型。(不清楚是哪个对象的实例)(无法判断工厂模式的指向问题,构造函数可以)

2.没有原型,占用内存。(构造函数节约性能,用的多一些)

<script>
    // 工厂模式
    function Person(name) {
        let obj = {};
        obj.name = name;
        obj.age = 20;
        obj.fn = function() {
            console.log("fn..");
        }
    }
    let zhangsan = Person("张三");
    // console.log(zhangsan.constructor===Person);

    // 构造函数;
    // function Person(){
    //     this.name = name;
    //     this.age = 20;
    // }
    // Person.prototype.fn = function(){
    //     console.log("fn...");
    // }
    // let zhangsan  = new Person("张三");
    // console.log( zhangsan.constructor===Person);
    
    let str = "abc";
    console.log(str.constructor === String);
</script>

原型链

对象之间的继承关系,在JavaScript中是通过(原型对象)prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,称之为原型链;

1.当访问一个对象的属性或方法时,会先在对象自身上查找属性或方法是否存在,如果存在就使用对象自身的属性或方法。如果不存在就去创建对象的构造函数的原型对象中查找 ,依此类推,直到找到为止。如果到顶层对象(Object)中还找不到,则返回 undefined。

2.原型链最顶层为 Object 构造函数的 prototype 原型对象,给 Object.prototype 添加属性或方法可以被除 null 和 undefined 之外的所有数据类型对象使用。

在这里插入图片描述

<script>
    // 构造函数
    function Foo(name) {
        this.name = name;
        this.age = 20;
        this.test = "你好111";//构造函数的属性
    }
    //原型对象;
    Foo.prototype.fn = function() {
            console.log("f");
     }
    Foo.prototype.test = "hello";//原型对象的属性
    Object.prototype.test = "你好222";//原型链顶层的属性
    
    let newFoo = new Foo("张三");
    console.log(newFoo.test);// 先查找构造函数内是否有test,再查找原型对象内是否有test,最后到原型链顶层查找受否有test,都无则返回undefined
    
    // let obj  = new  Object();
    console.log(Object.prototype.__proto__);//查找原型链顶层
    
</script>

构造函数继承(类继承)

  • 继承:子类继承父类所有属性和行为,父类不受影响。
  • 目的:找到类之间的共性精简代码
  • call,apply,bind------改变this指向
  • call()-----第一个参数是改变this指向,剩下参数是传参,call接收多个参数
  • apply()-----第一个参数是改变this指向,剩下参数是传参,apply接收数组
  • bind()-----第一个参数是改变this指向,bind返回新的函数,再次执行函数
// 构造函数继承
function Person(name){
    this.name = name;
    this.eyes = "两只";
    this.legs = "两条";
}
function Student(name){
    Person.call(this,name)
    this.className = "二班";
}
let newPerson = new Student("张三");
console.log(newPerson.className);
  • 简单原型继承,出现影响父类的情况;

    function Person(name){
        this.name = name;
        this.eyes = "两只";
        this.legs = "两条";
    }
    function Student(name){
        Person.call(this,name)
        this.className = "二班";
    }
    Student.prototype = Person.prototype  //直接赋值
    
// call、apply、bind  ----改变this指向,实现构造函数的继承(类的继承)
<script>
function foo(name,age) {
    console.log(this,"姓名是"+name+"年龄是"+age);
}
// foo();
let obj = {
    name:"张三"
}
//第一个参数是改变this指向,剩余是传参
// foo.call(obj,"张三",20);call接收多个参数
// foo.apply(obj,["张三",20]);apply接收数组
// foo.bind(obj)("张三",20);bind返回新的函数,再次执行函数

</script>
<script>
// 构造函数的继承;
function Dad(name,age) {
    this.name = name;
    this.age = age;
    this.money = "100000";
}

function Son(name,age) {
	// 父类与子类this互通
    // Dad.call(this,name,age);
    // Dad.apply(this,[name,age])
    Dad.bind(this)(name,age);
    this.sex = "男";
}

let zhangsann  = new Son("张三",20);
console.log( zhangsann.money);//通过改变this指向实现继承,拿到父类的money
console.log( zhangsann.sex);

</script>
// 原型的继承
<script>
    // 继承;
    function Dad(name, age) {
        this.name = name;
        this.age = age;
        this.money = "100000";
    }
    //原型上的方法
    Dad.prototype.fn = function() {
        console.log("fn");
    }


    function Son(name, age) {
        Dad.call(this, name, age);
        this.sex = "男";
    }
    
    Son.prototype = Dad.prototype;//这样直接等于会涉及到传址问题,会互相影响
    
    Son.prototype.fn = function() {// 覆盖父类的fn
        console.log("重写的fn");
    }
    
    let zhangsan = new Son("张三", 20);
    // console.log( zhangsann.money);
    // console.log( zhangsann.sex);
    zhangsan.fn();//重写的fn
    
    let zhangyi = new Dad("张一", 20);
    zhangyi.fn();//重写的fn
</script>

原型的深拷贝继承

  • 传值和传址问题

    • 基本数据类型:Number、String、Boolean、Null、Undefined
    • 复杂数据类型/引用数据类型:Array、Date、Math、RegExp、Object、Function等
  • JSON序列化的不足

    如果拷贝对象包含函数,或者undefined等值,此方法就会出现问题
    
  • 浅拷贝和深拷贝

    //递归深拷贝
    function deepCopy(obj){
        let newObj = Array.isArray(obj)?[]:{};
        for(let key in obj){
            if(obj.hasOwnProperty(key)){
                if(typeof obj[key] == "object"){
                    newObj[key] = deepCopy(obj[key]);
                }else{
                    newObj[key] = obj[key];
                }
            }
        }
        return newObj;
    }
    
<script>
//复杂数据类型传址;(传址问题)
//(复杂数据类型涉及传址问题,会互相影响,解决相互影响的办法(解决传址问题):深拷贝)
// let DadProto = {
//     name:"张三",
//     age:20
// }
// let SonProto = DadProto;
// SonProto.name = "李四";
// console.log(SonProto);{name:"李四",age:20}
// console.log(DadProto);{name:"李四",age:20}



// 简单数据类型:传值;(重新开辟内存地址,不会互相影响)
// let a = 10;
// let b = a;
// b = 20;
// console.log(a);10



// 深拷贝;(解决传址问题)【重新在内存内开辟新地址】
// let DadProto = {
//     name:"张三",
//     age:20,
//     fn:function() {
//         console.log("fn..");
//     },
//     test:undefined
// }

// 使用序列化进行深拷贝时,需要注意方法和undefined会丢失
// let SonProto = JSON.parse(JSON.stringify( DadProto));
// SonProto.name = "李四";
// console.log(DadProto);
// console.log(SonProto);




// 深拷贝 
let obj = {
    name:"张三",
    age:20,
    fn:function() {
        console.log("fn..");
    },
    test:undefined,
    arr:[],
}

let obj2 = deepCopy( obj);

obj2.name = "李四";
console.log(obj2);
console.log(obj);

//(封装一个深拷贝:开辟新的地址)
function deepCopy(obj) {
    let newObj = Array.isArray(obj)?[]:{};//查看obj是否是数组,是数组则新定义一个数组,否则定义一个对象
    for(let key in obj){//循环遍历obj
        if(obj.hasOwnProperty(key)){//不需要循环原型和原型链上的属性和方法,只拿自身的属性;判断key循环出来的属性是不是自身obj的属性和方法
            if(typeof obj[key] === "object"){//若是对象,继续遍历
                newObj[key] = deepCopy(obj[key]) ;
            }else{
                newObj[key] = obj[key];//存入新定义的newObj内
            }
        }
    }
    return newObj;
}

</script>

原型的继承

  • 深拷贝继承
// 原型深拷贝继承
<script>
function deepCopy(obj) {
    let newObj = Array.isArray(obj)?[]:{};
    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            if(typeof obj[key] === "object"){
                newObj[key] = deepCopy(obj[key]) ;
            }else{
                newObj[key] = obj[key];
            }
        }
    }
    return newObj;
}

function Dad(name,age) {
    this.name = name;
    this.age = age;
    this.money = "100000";
}
Dad.prototype.fn = function () {
    console.log("喜欢象棋");
}

function Son(name,age) {
    Dad.call(this,name,age);
    this.sex = "男";
}

// 深拷贝继承
Son.prototype =deepCopy(Dad.prototype);

Son.prototype.fn = function () {
    console.log("喜欢篮球");
}

let zhangsan  = new Son("张三",20);
// console.log( zhangsann.money);
// console.log( zhangsann.sex);
zhangsan.fn();
let zhangyi  = new Dad("张一",50);
zhangyi.fn();

</script>
  • 组合继承

    function Dad(){
        this.name = "张三";
    }
    Dad.prototype.hobby = function(){
        console.log("喜欢篮球");
    }
    function Son(){
        Dad.call(this);
    }
    let F = function(){}
    F.prototype = Dad.prototype;
    Son.prototype = new F();
    Son.prototype.constructor = Son;
    
    let newSon = new Son();
    newSon.hobby();
    
<script>
// 组合继承
function Dad(name,age) {
    this.name = name;
    this.age = age;
    this.money = "100000";
}
Dad.prototype.fn = function () {
    console.log("喜欢象棋");
}

function Son(name,age) {
    Dad.call(this,name,age);
    this.sex = "男";
}

// Son.prototype =deepCopy(Dad.prototype);
// 组合继承,等同于以上
let Link = function(){};// 空构造函数
Link.prototype = Dad.prototype;
Son.prototype = new Link();
Son.prototype.constructor = Son;


Son.prototype.fn = function () {
    console.log("喜欢篮球");
}
let zhangsan  = new Son("张三",20);
// console.log( zhangsann.money);
// console.log( zhangsann.sex);
zhangsan.fn();
let zhangyi  = new Dad("张一",50);
zhangyi.fn();


</script>

包装对象

  • 除过null,undefined,基本类型都有自己对应的包装对象:String Number Boolean
  • 包装对象把所有的属性和方法给了基本类型,然后包装对象消失
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值