javascript.basics icon indicating copy to clipboard operation
javascript.basics copied to clipboard

JS面向对象总结【封装,继承,多态,this,prototype】(笔记)

Open Kelichao opened this issue 9 years ago • 4 comments

了解面向对象三大特征之前,对于抽象的了解十分重要,在定义一个类的时候,实际上就是把一类事物共有的属性和行为提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。

封装(encapsulation )

function Person(name, age) {
    this.name = name;
    var age = age;// 在实例中无法被调用
}
var p1 = new Person("Bob", 20);
console.log(p1) // Person ->{name: "Bob"}  无法访问到age属性,这就叫被封(装)起来了。

访问封装属性的方法--特权方法

function Person(age) {
    var age = age;// 私有变量
    this.showAge = function() {// 特权方法
        console.log(age);
    };
}
var p1 = new Person(20);// 新建对象p1
p1.showAge();// -> 20  这个20是闭包,在闭包笔记处会详解。
// 如果不理解闭包,按照我自己的思路很难去解释:
// 为什么这个函数体里面存了一份age的数据。

但是如果使用prototype来写的函数,无法访问私有变量

Person.prototype.myAge = function() {
    console.log(age);
};
var p1 = new Person(20);// 新建对象p1
p1.myAge();// 报错 age is not defined

其实这也印证了:闭包就是函数里面包函数,由于prototype是通过函数名,指到其他内存空间独立的函数体,因此没法取得闭包的作用域变量。

继承

将不同类的相同之处取出封装成独立的类

1.对象冒充方法 (有缺陷,在继承笔记详细介绍)

function Stu() {
    this.name = "学生";
}
Stu.prototype.free = function() {
    console.log("free");
};
function MidStu() {
    this.stu = Stu;// 将Stu函数体赋值给stu;

    /*
     * 执行stu里面的函数体
     * 就相当于执行了这句话 :this.name = "学生";
     */
    this.stu();// 通过对象冒充,来实现继承。
delete this.stu;// 删除对象的引用。
}

var stu1 = new MidStu();
console.log(stu1);  // 拥有这个属性 this.name = "学生";
stu1.free(); //prototype的free这个属性不存在

多态

所谓多态,就是指一个引用类型在不同情况下的多种状态。在java中多态是指通过指向父类的引用,来调用在不同子类中实现的方法。 js实际上是无态的,是一种动态语言,一个变量的类型是在运行的过程中由js引擎来决定的,所以说js天生就支持多态。

image

    // 主人类
    function Master() {
        // 主人玩耍动物
        this.feed = function(animal) {
            console.log("主人玩耍" + animal.name);
            console.log("类:" + animal.constructor);
        };
    }

    // 猫类
    function Cat(name) {
        this.name = name;
    }

    // 狗类
    function Dog(name) {
        this.name = name;
    }

    var cat = new Cat("小猫咪");
    var dog = new Dog("小狗狗");
    var master = new Master();

    master.feed(cat);
    master.feed(dog);
    // 这样做的优点在于Master不需要改变,如果要扩展加入其它的动物,
    // 只需要加一个猴子类就好了

Javascript-面向(基于)对象编程-介绍

  • Javascript-是采用基于对象(面向对象)来编程的。
  • Javascript-中没有Class类(原型对象) tip:有人直接就叫类了。

如果要得到一个cat1(猫1)对象

var cat1 = {};
cat1.age = 1;
cat1.name = "小白";
cat1.voice = "喵";
// 得到 cat1 = {age: 1, name: "小白",voice :"喵"};

但是要写出cat2,cat3则需要很多的代码

var cat2 = {};
cat2.age = 2;
cat2.name = "小白";
cat2.voice = "喵";
// 得到 cat2 = {age: 2, name: "小白",voice :"喵"};

var cat3 = {};
cat3.age = 3;
cat3.name = "小白";
cat3.voice = "喵";
// 得到 cat3 = {age: 3, name: "小白",voice :"喵"};

接着来描述下创建类(原型对象)的方式:5种

  1. 工厂方法--使用new Object创建对象并添加相关属性。
  2. 使用构造函数来定义类(原型对象)。
  3. 使用prototype
  4. 构造函数及原型混合方式
  5. 动态原型方式

重点是第二种方式写法

function 类名/原型对象名 (){

} // 创建对象 var 对象名 = new 类名();

function aaa() {
    console.log(this.bbb);// 可以输出bbb这个函数,说明bbb函数也被提前执行
    this.a = console.log(111);
}

// 注意点,bbb这个函数在new的时候会被提前加载,在console.log(this.bbb)这句话中可以输出函数体
aaa.prototype.bbb = function() {
    console.log(222);
};

new aaa();

那么如何判断实例是属于什么类?

// 我自己定义的类
function Person() {}
var Bob = new Person();
console.log(Bob.constructor);
// function Person() {}
console.log(typeof Bob);
// object

// 系统的类number
var bbb = 12345; // -> var bbb = new Nmuber(12345);
console.log(bbb.constructor);
// function Number() { [native code] }
console.log(typeof bbb);
// number

Javascript-中一切都是对象

function Person() {}
console.log(Person.constructor);

//  函数的类为 Function() { [native code] }

如何判断一个对象实例是不是Person类型

function Person() {}
var Bob = new Person();
// instanceof  实例
console.log(Bob instanceof Person) // true   instanceof 还可以用来判断复杂类型对象的归属
console.log(Person === Bob.constructor);   // true

delete 用于删属性 delete cat1.name 注:不能直接删cat1对象,不能删除window上面的属性。

Kelichao avatar Oct 20 '16 02:10 Kelichao

this 的提出

function Person() {

}
var p1 = new Person();
p1.name = "顺平";

var p2 = new Person();
console.log(p2.name); // -> undefined

当我们创建一个对象后,希望该对象自动拥有某些属性,比如有个初始 name

function Person() {
    this.name = "初始名"
}
var p1 = new Person();
var p2 = new Person();
console.log(p1.name + " : " + p2.name); // -> “初始名”,两个实例对象的name属性相互对立不影响。

我在这里先对比下有new 跟没有new的区别

function Person() {
        this.name = "初始名";
        console.log(this);
} 

var p1 = Person(); // 打出的是window对象
var p1 = new Person();  //1 打出的是 新Person;2而且返回值为this。其他不同点暂时还没有发现。

有new下的this指向 image

在我目前看来,new 一个构造函数,返回的是一个this,本质上我觉得是重新开辟了一个内存空间给新生的Person然后通过给变量赋值,以及开辟了一个空间给原型对象prototype,以用来存储。比如p1 = 新Person,拿到新Person的地址,得到对象所拥有的方法。

function Person() {
        this.name = "初始名";
}
console.log(new Person() ) // 返回值是this

p只是记录了这个引用对象的地址。 image p记录的是ox1234的地址,所以在调用abc()的时候调用的是ox1234的abc,this指向的就是ox1234 image

如果将返回值改为引用类型,则会改变返回值,注:return 123;这种不生效

function Person() {
        this.name = "初始名"
        return [];
}
console.log(new Person()) // ->[ ]

this只能在类的内部使用。

总结一下,是什么决定this最终的指向,this最终的指向永远是离方法最近的那个引用类型的那个对象。

var name = "window";
var myname = function() {
    console.log(this.name);
};
var value = {
    a: {
        name: "a",
        myname: function() {
            console.log(this.name);
        },
        b: {
            name: "b",
            myname: function() {
                console.log(this.name);
            }
        }
    }
};

/*
* window
* a
* b
* 这三者都是引用对象
*/
window.myname();// window
window.value.a.myname();// a
window.value.a.b.myname();//b

Kelichao avatar Oct 20 '16 05:10 Kelichao

成员函数(成员方法)

function Person(name, age) {

    /*
     * 传入实际的参数去初始化属性时用到。
     * 适用于每个对象都有的,但是里面的内容不一样的。
 * 好处是可以在初始化对象(new)时使用传入的参数
 * 缺点是每个对象都有自己的一份配置,不共用 
     */
    this.name = name;
    this.age = age;
}

var p1 = new Person("林冲", 30);
var p2 = new Person("宋江", 40);
console.log(p1.name + ":" + p2.name); // -> 林冲:宋江

对于函数赋值, 之前没有尝试过这种方法,也是可以用的

var x = function aaa() {
   console.log("good");
};
console.log(x);
// -> 打出的是有名字的函数,跟匿名函数不一样
//function aaa() {
//   alert();
//}
x(); // -> good
// var x = aaa= function() {
//  console.log()
// }

//等同
function aaa() {
   console.log("good");
}
var x = aaa;

验证通过this制造的函数体是否独立

function Person() {
    this.aaa = function(value) {};
}

var p1 = new Person();
var p2 = new Person();
console.log(p1.aaa === p2.aaa);  // false

prototype 原型链 (也是成员方法)

通过shout函数名 来调用函数体,公用 image 验证通过this制造的函数体是否独立

function Person() {}
/*
 *  缺点是不可以在初始化对象(new)时使用传入的参数
 * 优点是每个对象都公用这有一份属性,节流 
*
*/
Person.prototype.aaa = function(value) {};

var p1 = new Person();
var p2 = new Person();
console.log(p1.aaa === p2.aaa); // true

Kelichao avatar Oct 20 '16 08:10 Kelichao

写得不错,star

hufans avatar Jun 13 '18 15:06 hufans

了解面向对象三大特征之前,对于抽象的了解十分重要,在定义一个类的时候,实际上就是把一类事物共有的属性和行为提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。

封装(encapsulation )

function Person(name, age) {
    this.name = name;
    var age = age;// 在实例中无法被调用
}
var p1 = new Person("Bob", 20);
console.log(p1) // Person ->{name: "Bob"}  无法访问到age属性,这就叫被封(装)起来了。

访问封装属性的方法--特权方法

function Person(age) {
    var age = age;// 私有变量
    this.showAge = function() {// 特权方法
        console.log(age);
    };
}
var p1 = new Person(20);// 新建对象p1
p1.showAge();// -> 20  这个20是闭包,在闭包笔记处会详解。
// 如果不理解闭包,按照我自己的思路很难去解释:
// 为什么这个函数体里面存了一份age的数据。

但是如果使用prototype来写的函数,无法访问私有变量

Person.prototype.myAge = function() {
    console.log(age);
};
var p1 = new Person(20);// 新建对象p1
p1.myAge();// 报错 age is not defined

其实这也印证了:闭包就是函数里面包函数,由于prototype是通过函数名,指到其他内存空间独立的函数体,因此没法取得闭包的作用域变量。

继承

将不同类的相同之处取出封装成独立的类

1.对象冒充方法 (有缺陷,在继承笔记详细介绍)

function Stu() {
    this.name = "学生";
}
Stu.prototype.free = function() {
    console.log("free");
};
function MidStu() {
    this.stu = Stu;// 将Stu函数体赋值给stu;

    /*
     * 执行stu里面的函数体
     * 就相当于执行了这句话 :this.name = "学生";
     */
    this.stu();// 通过对象冒充,来实现继承。
delete this.stu;// 删除对象的引用。
}

var stu1 = new MidStu();
console.log(stu1);  // 拥有这个属性 this.name = "学生";
stu1.free(); //prototype的free这个属性不存在

多态

所谓多态,就是指一个引用类型在不同情况下的多种状态。在java中多态是指通过指向父类的引用,来调用在不同子类中实现的方法。 js实际上是无态的,是一种动态语言,一个变量的类型是在运行的过程中由js引擎来决定的,所以说js天生就支持多态。

image

    // 主人类
    function Master() {
        // 主人玩耍动物
        this.feed = function(animal) {
            console.log("主人玩耍" + animal.name);
            console.log("类:" + animal.constructor);
        };
    }

    // 猫类
    function Cat(name) {
        this.name = name;
    }

    // 狗类
    function Dog(name) {
        this.name = name;
    }

    var cat = new Cat("小猫咪");
    var dog = new Dog("小狗狗");
    var master = new Master();

    master.feed(cat);
    master.feed(dog);
    // 这样做的优点在于Master不需要改变,如果要扩展加入其它的动物,
    // 只需要加一个猴子类就好了

Javascript-面向(基于)对象编程-介绍

  • Javascript-是采用基于对象(面向对象)来编程的。
  • Javascript-中没有Class类(原型对象) tip:有人直接就叫类了。

如果要得到一个cat1(猫1)对象

var cat1 = {};
cat1.age = 1;
cat1.name = "小白";
cat1.voice = "喵";
// 得到 cat1 = {age: 1, name: "小白",voice :"喵"};

但是要写出cat2,cat3则需要很多的代码

var cat2 = {};
cat2.age = 2;
cat2.name = "小白";
cat2.voice = "喵";
// 得到 cat2 = {age: 2, name: "小白",voice :"喵"};

var cat3 = {};
cat3.age = 3;
cat3.name = "小白";
cat3.voice = "喵";
// 得到 cat3 = {age: 3, name: "小白",voice :"喵"};

接着来描述下创建类(原型对象)的方式:5种

  1. 工厂方法--使用new Object创建对象并添加相关属性。
  2. 使用构造函数来定义类(原型对象)。
  3. 使用prototype
  4. 构造函数及原型混合方式
  5. 动态原型方式

重点是第二种方式写法

function 类名/原型对象名 (){ } // 创建对象 var 对象名 = new 类名();

function aaa() {
    console.log(this.bbb);// 可以输出bbb这个函数,说明bbb函数也被提前执行
    this.a = console.log(111);
}

// 注意点,bbb这个函数在new的时候会被提前加载,在console.log(this.bbb)这句话中可以输出函数体
aaa.prototype.bbb = function() {
    console.log(222);
};

new aaa();

那么如何判断实例是属于什么类?

// 我自己定义的类
function Person() {}
var Bob = new Person();
console.log(Bob.constructor);
// function Person() {}
console.log(typeof Bob);
// object

// 系统的类number
var bbb = 12345; // -> var bbb = new Nmuber(12345);
console.log(bbb.constructor);
// function Number() { [native code] }
console.log(typeof bbb);
// number

Javascript-中一切都是对象

function Person() {}
console.log(Person.constructor);

//  函数的类为 Function() { [native code] }

如何判断一个对象实例是不是Person类型

function Person() {}
var Bob = new Person();
// instanceof  实例
console.log(Bob instanceof Person) // true   instanceof 还可以用来判断复杂类型对象的归属
console.log(Person === Bob.constructor);   // true

delete 用于删属性 delete cat1.name 注:不能直接删cat1对象,不能删除window上面的属性。

WriteProtectX avatar Sep 14 '18 02:09 WriteProtectX