liu97

好好学习,天天向上

  • 博客
  • 随笔
  • 关于我
所有文章 友链

liu97

好好学习,天天向上

  • 博客
  • 随笔
  • 关于我

js面向对象笔记

2018-02-27

在学习js面向对象时,每次学了不运用,不过多久就会忘,所以今天跟着阮一峰老师的理解记下笔记,以下为学习时自己的理解。

1.封装

一、构造函数的实例对象的constructor指向构造函数

1
2
3
4
5
6
7
function Cat(name,color){
this.name=name;
this.color=color;
}
let cat1 = new Cat('tom' , 'orange');
let cat2 = new Cat('jack' , 'black');
console.log(cat1.constructor == Cat) // true

其实这里是有问题的,cat1.constructor == Cat返回为true的原因是:cat1并没有constructor属性,但它的原型链Cat.prototype上有constructor属性且指向Cat构造函数。当我们访问cat1.constructor时它自身没有就往原型链上找,一找就找到了Cat.prototype,于是cat1.constructor == Cat返回true。

有图为证

二、构造函数有prototype属性,指向一个对象,这个对象的所有属性和方法都会被构造函数的实例继承

1
2
3
4
Cat.prototype.type = "猫科动物";
Cat.prototype.action = ()=> console.log("吃老鼠");

cat1.type == "猫科动物" // true

这时每个实例的的type和action都是指向同一内存地址,也就是prototype对象,节省了内存。
当实例改变了prototype上的属性时,只会改变当前实例的属性,prototype的属性并不会更改,也就不会影响其他实例,方法同上,以下也只说属性,方法同样。
原因:既然实例会继承prototype指向的对象,于是cat1.proto == Cat.prototype,当访问实例属性时,先会在实例上找有没有该属性,没有的话便顺着原型链往上一级找,上一级也就是Cat.prototype。当实例改变了prototype上的属性(cat1.type),相当于在实例上添加了属性,并不会改变原型链上祖辈的属性,于是下次访问该属性(cat1.type)时,在实例上找到了该属性,便不会再往原型链上级找。

1
2
3
4
cat1.type = "哺乳动物";
console.log(cat1.type) // "哺乳动物"
console.log(cat2.type) // "猫科动物"
console.log(Cat.prototype.type) // "猫科动物"

三、几个实用的验证方法

1
2
3
4
5
6
7
8
9
10
11
isPrototypeOf() 验证实例与prototype之间的关系
console.log(Cat.prototype.isPrototypeOf(cat1)) // true

hasOwnProperty() 验证是实例上的属性还是prototype上的属性
console.log(cat1.hasOwnProperty("type")) // true
console.log(cat2.hasOwnProperty("type")) // false

in运算符 验证某个实例是否含有某个属性,不管是不是本地属性
console.log("type" in cat1) // true
console.log("type" in cat2) // true
console.log("haha" in cat1) // false

2.构造函数的继承

一、构造函数绑定
用call或者apply的方法将父对象的构造函数绑定在子对象的构造函数中。这种方法只能继承构造器里的属性和方法

1
2
3
4
5
6
7
8
function Animal(){
this.species = "动物";
}
function Cat(name,color){
Animal.apply(this,arguments); // 绑定
this.name=name;
this.color=color;
}

二、prototype修改
将子对象的构造函数的prototype直接指向父对象的构造函数的实例。由于直接修改了prototype指向的对象,导致prototype的constructor的值变为父对象的构造函数,每当访问实例或者prototype的constructor属性,都会调用prototype对象的constructor属性,这样当然会破坏constructor的原本设计初衷,于是也要讲prototype的constructor重新指向子对象的构造函数。这种方法能继承构造器里的属性和方法,也能继承原型链上的属性和方法

1
2
3
4
5
6
7
8
9
Cat.prototype = new Animal();
let cat3 = new Cat("aimi","white");
console.log(Cat.prototype.__proto__ == Animal.prototype) // true
console.log(Cat.prototype.constructor == Animal) // true
console.log(cat3.constructor == Animal) // true
console.log(cat3.__proto__ == Cat.prototype) // true
Cat.prototype.constructor = Cat; //修改prototype.constructor
let cat3 = new Cat("aimi","white");
console.log(cat4.constructor == Cat) // true

三、直接赋值prototype
将父对象的prototype直接赋值给子对象的prototype,实现了继承,这种方法有一个很大的特性就是Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。这种方法只能继承原型链上的属性和方法

1
2
3
4
5
6
7
function Animal(){ }
Animal.prototype.species = "动物";
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黄色");
console.log(cat1.species); // "动物"
console.log(Animal.prototype.constructor); // "Cat"

四、利用空对象作为中介
由于”直接继承prototype”存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。这种方法只能继承原型链上的属性和方法

1
2
3
4
5
6
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
console.log(Animal.prototype.constructor); // Animal
console.log(Cat.prototype.__proto__ == Animal.prototype); // Animal

我们将上面的方法,封装成一个函数,便于使用。这个extend函数,就是YUI库如何实现继承的方法

1
2
3
4
5
6
7
8
function extend(Child, Parent) {
  var F = function(){};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
  Child.uber = Parent.prototype; // 为子对象设一个uber属性,这个属性直接指向父对象的prototype属性

}

五、拷贝继承
把父对象的所有属性和方法,拷贝进子对象,也能够实现继承。这种方法只能继承原型链上的属性和方法

1
2
3
4
5
6
7
8
9
10
function Animal(){}
Animal.prototype.species = "动物";
function extend2(Child, Parent) {
  var p = Parent.prototype;
  var c = Child.prototype;
  for (var i in p) {
    c[i] = p[i];
  }
  c.uber = p;
}

3.非构造函数的继承

一、object()方法
这个object()函数,其实只做一件事,就是把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。类似第二节里《四、利用空对象作为中介》

1
2
3
4
5
6
7
8
9
10
let Chinese = {
  nation:'中国'
};
function object(parent){
function F(){};
F.prototype = parent;
return new F();
}
let Doctor = object(Chinese);
Doctor.career = '医生';

二、浅拷贝
除了使用”prototype链”以外,还有另一种思路:把父对象的属性,全部拷贝给子对象,也能实现继承。类似第二节里《五、拷贝继承》

1
2
3
4
5
6
7
8
function extendCopy(parent){
var c = {};
for(let i in parent){
c.i = parent.i;
}
c.uber = parent;
return c;
}

但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。

1
2
3
4
5
Chinese.birthPlaces = ['北京','上海','香港'];
var Doctor = extendCopy(Chinese);
Doctor.birthPlaces.push('厦门');
console.log(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
console.log(Chinese.birthPlaces); //北京, 上海, 香港, 厦门

所以,extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做”浅拷贝”。这是早期jQuery实现继承的方式。

三、深拷贝
所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepCopy(p, c) {
  var c = c || {};
  for (var i in p) {
    if (typeof p[i] === 'object') {
      c[i] = (p[i].constructor === Array) ? [] : {};
      deepCopy(p[i], c[i]);
    } else {
       c[i] = p[i];
    }
  }
  return c;
}
var Doctor = deepCopy(Chinese);
Chinese.birthPlaces = ['北京','上海','香港'];
Doctor.birthPlaces.push('厦门');
console.log(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
console.log(Chinese.birthPlaces); //北京, 上海, 香港

目前,jQuery库使用的就是这种继承方法。

4.class继承

新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。class的定义包含了构造函数constructor和定义在原型对象上的函数hello()(注意没有function关键字),这样就避免了Student.prototype.hello = function () {…}这样分散的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student{
constructor(name){
this.name = name;
}
hello(){
console.log("hello");
}
}
let std1 = new Student("xiaoming");
let std2 = new Student("lining");
console.log(std1.hello == std2.hello); //true
class PrimaryStudent extend Student{
constructor(name,grade){
super(name);
this.grade = grade;
}
myGrade(){
return this.grade;
}
}

感谢链接

Javascript 面向对象编程
廖雪峰的官方网站

赏

谢谢你请我吃糖果

支付宝
微信
  • js
  • Blogs

扫一扫,分享到微信

微信分享二维码
css浮动(float)
git学习笔记
  1. 1. 1.封装
  2. 2. 2.构造函数的继承
  3. 3. 3.非构造函数的继承
  4. 4. 4.class继承
  5. 5. 感谢链接
Like Issue Page
Error: Comments Not Initialized
Login with GitHub
Styling with Markdown is supported
Powered by Gitment
© 2020 liu97
Hexo Theme Yilia by Litten
  • 所有文章
  • 友链

tag:

  • hexo
  • react
  • js
  • ajax
  • chrome
  • css
  • git
  • jquery
  • livereload
  • wamp
  • 数据结构
  • 算法
  • web缓存
  • 移动端
  • video
  • canvas
  • 笔试
  • webpack
  • 前端
  • 随笔
  • html5

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 廖雪峰
  • 阮一峰
  • 大漠
  • 张鑫旭