面向对象

面向对象

1.面向对象概念

/*
面向对象概念:
    一、面相过程:注重解决问题的步骤,分析问题需要的每一步,实现函数依次调用;
    二、面相对象:是一种程序设计思想。将数据和处理数据的程序封装到对象中;
    三、面相对象特性: 抽象、 继承、封装、多态
    优点:提高代码的复用性及可维护性;
*/
/* 
    小明去餐厅吃饭: 面向过程: 1.小明去餐厅看菜单点餐吃饭
    面向对象: 1.小明(走、看、点餐、吃) 2.餐厅(菜单);
    研究对象之间的关系: 小明.走餐厅.菜单 小明.看 小明.点餐 小明.吃;
*/

2.JS里的对象是什么

// 对象创建

// 1.字面量方式
let str = 'name';

let obj = {
    // 属性
    [str]:"张三",
    // name:"张三",
    age:20,
    /* hobby(){
        console.log("喜欢打篮球");
    }, */
    // 方法
    hobby:function(){
        console.log("喜欢打篮球")
    }
}

// 2.构造函数;
// let obj = new Object();
// obj.name = "张三";
// obj.age = 20;
// /* obj.hobby = function(){
//     console.log("喜欢篮球");
// } */
// obj.hobby = ()=>{
//     console.log("喜欢篮球");
// }
// obj.hobby();
// console.log(obj);

// 3.Object.create();属性方法放在原型上;
// let obj = Object.create({
//     name:"张三",
//     age:20,
//     hobby(){
//         console.log("喜欢篮球")
//     }
// });
// console.log(obj); //在__proto__

// 对象的调用
// console.log(obj.name);//属性
// obj.hobby();//方法
console.log( obj["name"] );//属性

// let str = "name";
// // console.log(obj.str);
// console.log( obj[str] );

3.工厂模式

// 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;//出厂;
}
/* 
总结:工厂模式;
1.创建一个对象
2.为对象添加一些属性,方法
3.然后把对象 return回来
*/

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

4.new运算符

// let str = "";//[],{}...
// let str = new String();

/* 
new 运算符
    1.执行函数
    2.自动创建一个空对象
    3.把空对象和this绑定
    4.如果没有返还(return),隐式返回this
*/
// function test(){
//     console.log("test");
// }
// test();//执行函数
// new test();//执行函数 -> ()是否需要传参
// new test;//执行函数

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

//     // return this;
// }
// new Test();

// 当new配合工厂模式时:
function Person(name,age,hobby){
    // let obj = {}; === this
    this.name = name;
    this.age = age;
    this.hobby = function(){
        console.log(hobby);
    }
    // return obj;
}

let zhangsan = new Person("张三",20,"喜欢篮球");
console.log(zhangsan.name);//属性
zhangsan.hobby();//方法

5.构造函数

/* 
构造函数
    1.首字大写;
        区分构造函数和普通函数
        也是为了模拟系统的对象
    2.this指向实例化对象;

*/
function Person(name){
    // this.num = 0;
    this.name = name;
    this.age = 20;
    this.hobby = function(){
        console.log("喜欢篮球");
    }
}
Person.num = 0;//静态成员~
Person.fu = function() {
    console.log("fn");
}
// new的过程也叫做: 实例化! -> 通过工厂模式通过new运算符改造成构造函数
let zhangsan = new Person("张三");

// 静态属性和方法; (属于类本身的);
// 假如想统计一下'类'实例化的次数!
Person.num ++;
// zhangsan.num ++;
// console.log(zhangsan.num);//1
let lisi = new Person("李四");
Person.num ++;
// lisi.num ++;
// console.log(lisi.num);//1
console.log(Person.num);//2
//但是它们两个都是 1,并不符合我们的需求~
// 所以我们可以把它们统计的时候,认为是我们的类的属性 -> Person.num
/* 
总结构造函数
1.首字大写.
2.this执行实例化对象
    在构造函数里面写的所有的属性和方法都是属于实例化对象的
    除了实例化对象的属性和方法,还有一些 静态的属性和方法 它们是属于构造函数(类)自身的!
*/

6.构造函数性能

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 它们是对象:不仅需要值一样,还需要引用(内存地址)一样
// 虽然它们的值是一样的,但是它们在内存里面都是分别开辟了两个地址!
// 所以我们当有很多的对象的时候:10000,它们就都开辟了这么多份,就很大程度的占据内存
// JS提供了一个公共的空间去存放我们相同的方法!更好的节约我们的内容~ -> 原型prototype

7.原型

function Person(name) {
    this.name = name;
    this.age = 20;
    // this.hobby = function() {
    //     console.log("喜欢篮球")
    // }
}
/*
    每一个构造函数,在实例化的过程中,都分为两份!!
        1.构造函数
        2.公共空间原型;
            原型里面的this也是执行实例化对象(new)
*/
// Person.prototype.hobby = function() {
//     console.log("喜欢篮球");
// }
// Person.prototype.fn = function(){
//     console.log("fn");
// }
/*
    原型的固有属性
    系统自动生成的,自己定义好的!
*/
// console.log( Person.prototype.constructor );//指向构造函数!
// Person.prototype.constructor === Person;
Person.prototype = {
    // 注意点:可以在原型中追加方法,但是不要原型里面进行覆盖属性
    hobby:function(){
        console.log("喜欢篮球");
    },
    // 这时候的constructor属性是已经被覆盖掉了!! 可以手动添加!
    constructor:Person
}

let zhangsan = new Person("张三");
console.log( zhangsan.constructor === Person );//true
/* 
    实例化对象也是由两部分构成的!
        1.是它自身的属性和方法 -> 构造函数里面
        2.对象也有自己的原型 __proto__
            它和prototype有什么关系呢?
                它是一个东西,就表现形式不一样
*/
// console.log(zhangsan);
// console.log(zhangsan.__proto__ === Person.prototype);//true
// let lisi = new Person("李四");

// console.log( zhangsan.hobby === lisi.hobby );//true

// 虽然我们自己写的构造函数,使用constructor属性并不是很多,但在系统的函数中使用constructor属性可以进行判断类型!!

// let str = new String("abd");
// console.log( str.constructor === String );//true

8.三者关系 原型,构造函数,对象

let temp;
function Person(name){
    this.name = name;
    this.age = 20;
    // temp = this;
}
Person.prototype.fn = function(){
    console.log("fn");
    temp = this;
}
console.log(Person.prototype.constructor===Person);

let zhangsan = new Person("张三");
// console.log(zhangsan);
// console.log(temp === zhangsan);//true
zhangsan.fn();
console.log(temp === zhangsan);//true

/* 
1.构造函数的原型:
    构造函数.prototype
2.原型里面可以有 属性和方法
3.构造函数得到对象:
    new(实例化)
        它也有自身的属性,方法和原型(__proto__)
    实例化之后
        属性就是 构造函数里面的属性
        原型的方法就放到了 __proto__ 里面
4.this都是执行实例化对象
5.constructor指向构造函数
*/

9.工厂模式对比构造函数

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

// 构造函数
function Person(name){
    this.name = name;
    this.age = 20;
}
Person.prototype.fn = function(){
    console.log("fn...");
}
let zhangsan2 = new Person("张三");

/* 
为什么构造函数比工厂函数用的更多呢?
1.构造函数有一个原型
    原型有公共空间可以放属性和方法,当有多个实例化对象的时候,它原型上面的方法都在公共空间里面,不会重新占用内容
    工厂模式,就没有这个功能!
2.创建对象的指向问题
    可以通过constructor指向构造函数
        在这里可以知道实例化对象是通过那个构造函数进行构造的
            所以我们也可以通过constructor进行判断类型!
*/

10.原型链

// 构造函数
function Foo(name){
    this.obj = name;
    this.age = 20;
    // this.test = "你好";
}
/* 
    原型也是一个对象: 原型对象->那所以它也有自身的属性和原型!
    原型链的形成:因为原型也是一个对象,对象也有自己的原型和属性...
    有原型链的存在他们就有一个 查找的规则
        就近原则:构造函数->原型->最终原型
        都没有就是undefined

*/
Foo.prototype.fn = function(){
    console.log("fn");
}
// Foo.prototype.test = "hello";
// Object.prototype.test = "你好2";

let newFoo = new Foo("张三");
console.log(newFoo.test)

// let obj = new Object();//每一个对象都是通过Object对象进行 创建的!
// console.log(Object.prototype.__proto__);//null -> 所以这里是最终的原型

11.call,apply,bind 修改this指向

function foo(name,age){
    console.log(this,"姓名是"+name+"年龄是"+age);
}
// foo();//window
let obj = {
    name:"张三"
}
// foo.call(obj,"张三",20);
// foo.apply(obj,["张三",20]);
foo.bind(obj)("张三",20);
/* 
call();
    foo.call();
    第一个参数:改变this指向
    其他参数:对应函数参数
apply()
    foo.apply();
    第一个参数:改变this指向
    其他参数:用数组形式传递
bind()
    foo.bind(obj)();
    第一个括号:改变this指向
    其他参数需要在第二个括号进行传入
*/

12.继承

构造函数的继承

// 继承
function Dar(name,age){//父类
    this.name = name;
    this.age = age;
    this.money = "100000";
}

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

let zhangsan = new Son("张三",20);
console.log( zhangsan.money,zhangsan.sex );

原型的继承

// 继承
function Dar(name,age){//父类
    this.name = name;
    this.age = age;
    this.money = "100000";
}
Dar.prototype.fn = function(){
    console.log("fn");
}

function Son(name,age){//子类
    // Dar.call(this,name,age);
    this.sex = "男";
}
Son.prototype = Dar.prototype;//可以继承,但是有传址问题
Son.prototype.fn = function(){
    console.log("重写的fn");
}

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

let lisi = new Dar("李四",21);
lisi.fn();//父类的原型也受到了影响

传值和传址

/* 
传址
    复杂的数据类型都会涉及到传址问题
        复杂的数据类型:除了简单的数据类型,其他的都是复杂数据类型!
            基本数据类型:字符串,数字,布尔值,null,undefined.
*/
// let DadProto = {//假如它是原型->因为原型也是对象!!
//     name:"张三",
//     age:20
// }

// let SonProto = DadProto;//涉及到了对象的引用关系->就是用到的是一个地址
// SonProto.name = "李四";
// console.log(SonProto.name);
// console.log(DadProto.name);

// 简单数据类型 : 传值 -> 它们都会重新去开辟一个地址
// let a = 10;
// let b = a;
// b = 20;
// console.log(a);//10

// 如何做到不相互响应呢 -> 深拷贝
// let DadProto = {
//     name:"张三",
//     age:20,
//     fn(){
//         console.log("fn..")
//     },
//     test:undefined
// }

// let SonProto = JSON.parse( JSON.stringify( DadProto ) );
// SonProto.name = "李四";
// console.log(DadProto)
// console.log(SonProto)
/* 
    简单的深拷贝: 
        JSON.stringify转成json
        JSON.parse把json转成字符串
        问题:
            1.丢失方法,丢失undefined
 */

// 深拷贝函数
let obj = {
    name:"张三",
    age:20,
    fn(){
        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)?[]:{};
    for(var 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 deepCopy(obj){
    let newObj = Array.isArray(obj)?[]:{};
    for(var 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 Dar(name,age){//父类
    this.name = name;
    this.age = age;
    this.money = "100000";
}
Dar.prototype.fn = function(){
    console.log("fn");
}

function Son(name,age){//子类
    // Dar.call(this,name,age);
    this.sex = "男";
}
Son.prototype = deepCopy(Dar);//深拷贝方式继承
Son.prototype.fn = function(){
    console.log("重写的fn");
}

let zhangsan = new Son("张三",20);
// console.log( zhangsan.money,zhangsan.sex );
zhangsan.fn();
let lisi = new Dar("李四",21);
lisi.fn();

组合继承

// 继承
function Dar(name,age){//父类
    this.name = name;
    this.age = age;
    this.money = "100000";
}
Dar.prototype.fn = function(){
    console.log("fn");
}

function Son(name,age){//子类
    // Dar.call(this,name,age);
    this.sex = "男";
}

let Link = function(){}
Link.prototype = Dar.prototype;
Son.prototype = new Link();
Son.prototype.constructor = Son;

Son.prototype.fn = function(){
    console.log("重写的fn");
}

let zhangsan = new Son("张三",20);
// console.log( zhangsan.money,zhangsan.sex );
zhangsan.fn();
let lisi = new Dar("李四",21);
lisi.fn();
posted @ 2020-01-03 19:41  JackAfan  阅读(218)  评论(0)    收藏  举报
setTimeout(function(){ let aImg = document.querySelectorAll("img"); aImg.forEach(img=>{ img.alt = "" }) console.log("去除img-alt成功") },1000)