blog icon indicating copy to clipboard operation
blog copied to clipboard

JS Object & Prototype

Open nanyang24 opened this issue 7 years ago • 0 comments

JavaScript 对象

因为JavaScript是基于原型(prototype)的,没有类的概念(ES6有了,这个暂且不谈),我们能接触到的都是对象,真正做到了一切皆为对象

Object

我们知道JavaScript有null、undefined、number、boolean、string五种简单类型,null和undefined分别表示没有声明和声明后没有初始化的变量、对象,是两个简单的值,其余三个有对应的包装对象Number、Boolean、String

其它的就都是object类型了,比如常用的Array、Date、RegExp等,我们最常用的Function也是个对象,虽然

typeof function(){}; // "function"

但是Function实例和其它类型的实例没有什么区别,都是对象,只不过typeof操作符对其做了特殊处理

在JavaScript中使用对象很简单,使用new操作符执行Obejct函数就可以构建一个最基本的对象

var obj = new Object();

我们称new 调用的函数为构造函数,构造函数和普通函数区别仅仅在于是否使用了new来调用,它们的返回值也会不同

所谓“构造函数”,就是专门用来生成“对象”的函数。它提供模板,作为对象的基本结构。一个构造函数,可以生成多个对象,这些对象都有相同的结构

我们可以通过.来为对象添加属性和方法

//1.原始模式,Object构造函数方式
obj.name = 'Byron';
obj.printName = function(){
    console.log(obj.name);
};

这么写比较麻烦,我们可以使用字面量来创建一个对象,下面的写法和上面等价

//1.原始模式,对象字面量方式
var obj = {
    name: 'Byron',
    printName: function(){
        console.log(obj.name);
    }
}

虽然Object构造函数和对象字面量都可以用来创建单个对象,但这两个方式有个明显缺点,就是使用同一接口会创建很多对象,产生大量重复代码。 而且如果想个性化,只能通过手工赋值,使用者必须详细了解对象

除了最普通的字面量和Object构造函数创建对象,JS 中还有其他几种创建对象的方法,现在最常用的方法是组合模式

工厂模式

//2.工厂模式,定义一个函数创建对象
function createObj(nick, age){
  var obj = {
      nick: nick,
      age: age,
      printName: function(){
            console.log(this.nick);
        }
  };
  return obj;
}

var obj3 = createObj('Byron', 30);
obj3.printName();

工厂模式就是批量化生产,这种方法通过创建一个函数来实现自动创建对象的过程,至于个性化通过参数实现,开发者不必关注细节,只需要传入指定参数即可

问题:

  • 构造出来的对象类型都是Object,没有解决对象识别的问题(对象的类型)
  • 每次使用都要创建一个独立的对象,浪费内存。

构造函数 模式

我们需要了解一些额外知识

  1. function作为构造函数(通过new操作符调用)的时候会返回一个类型为function的name的对象

  2. function可以接受参数,可以根据参数来创建相同类型不同值的对象

  3. function实例作用域内有一个constructor属性,这个属性就可以指示其构造器

new

new 运算符接受一个函数 F 及其参数:new F(arguments...)。这一过程分为三步:

  1. 创建一个新实例。这步是把一个空的对象的 proto 属性设置为 F.prototype 。
  2. 初始化实例。函数 F 被传入参数并调用,关键字 this 指向了新实例。
  3. 返回实例。
// 3.构造函数模式,为对象定义一个构造函数

function Person(nick, age){
    this.nick = nick;
    this.age = age;
    this.sayName = function(){
            console.log(this.nick);
    }
}
var p1 = new Person('Byron', 25);

instanceof

instanceof是一个操作符,可以判断对象是否为某个类型的实例

p1 instanceof Person; // true
p1 instanceof Object;// true

instanceof判断的是对象

1 instanceof Number; // false

问题:

  • 构造函数在解决了上面所有问题,同时为实例带来了类型 但可以注意到每个实例printName方法实际上作用一样,但是每个实例要重复一遍,不同实例的方法其实是不同的函数,大量对象存在的时候是浪费内存

原型模式

  • 任何函数使用new表达式就是构造函数
  • 每个函数都自动添加一个名称为prototype属性,这是一个对象
  • 每个实例都有一个内部属性 __proto__(规范中没有指定这个名称,但是浏览器都这么实现的) 指向其类型的prototype属性,类的实例也是对象,其__proto__属性指向“类”的prototype

prototype

img

通过图示我们可以看出一些端倪,实例可以通过__prop__访问到其类型的prototype属性,这就意味着类的prototype对象可以作为一个公共容器,供所有实例访问

抽象重复

我们刚才的问题可以通过这个手段解决

  1. 所有实例都会通过原型链引用到类型的prototype
  2. prototype相当于特定类型所有实例都可以访问到的一个公共容器
  3. 重复的东西移动到公共容器里放一份就可以了
//4.原型模式,直接定义prototype属性
function Person () {}
Person.prototype.name = 'Jack';
Person.prototype.age = 18;
Person.prototype.sayName = function () { alert(this.name); };
//4.原型模式,字面量定义方式
function Person () {}
Person.prototype = {
    name: 'Jack',
    age: 18,
    sayName: function () { alert(this.name); }
};

var p1 = new Person(); //name='Jack'
var p2 = new Person(); //name='Jack'

问题

  • 需要注意的是原型属性和方法的共享,即所有实例中都只是引用原型中的属性方法,任何一个地方产生的改动会引起其他实例的变化。

混合模式(组合使用 构造函数 和 原型 模式)

//5. 原型构造组合模式,
function Person (name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype = {
    hobby: ['running','football'];
    sayName: function () { alert(this.name); },
    sayAge: function () { alert(this.age); }
};

var p1 = new Person('Jack', 20);
//p1:'Jack',20; __proto__: ['running','football'],sayName,sayAge
var p2 = new Person('Mark', 18);
//p1:'Mark',18;__proto__: ['running','football'],sayName,sayAge

问题

  • 做法是将需要独立的属性方法放入构造函数中,而可以共享的部分则放入原型中,这样做可以最大限度节省内存而又保留对象实例的独立性。
  • 是最常用的模式

问题1: OOP 指什么?有哪些特性

Object-oriented programming的缩写,即面向对象程序设计,其中两个最重要的概念就是类和对象。类只是具备了某些功能和属性的抽象模型,类在实例化之后得到的实体就是对象。

特性:

  • 继承性:子类自动继承其父级类中的属性和方法,并可以添加新的属性和方法或者对部分属性和方法进行重写。继承增加了代码的可重用性。
  • 多态性:子类继承了来自父级类中的属性和方法,并对其中部分方法进行重写。
  • 封装性:将一个类的使用和实现分开,只保留部分接口和方法与外部联系。

问题2: 如何通过构造函数的方式创建一个拥有属性和方法的对象?

function People(name, age){
  this.name = name;
  this.age = age;
  this.printName: function(){
      console.log(this.name)
  }
}

var p1 = new People('ny', '22')

问题3: prototype 是什么?有什么特性

每创建一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

所以也可以说prototype就是通过调用构造函数而创建的那个对象实例的原型对象,原型对象可以让所有对象实例共享它所包含的属性和方法

问题4:画出如下代码的原型图

function People (name){
  this.name = name;
  this.sayName = function(){
    console.log('my name is:' + this.name);
  }
}

People.prototype.walk = function(){
  console.log(this.name + ' is walking');  
}

var p1 = new People('饥人谷');
var p2 = new People('前端');

问题5: 创建一个 Car 对象,拥有属性name、color、status;拥有方法run,stop,getStatus

function Car(name, color, status) {
        this.name = name;
        this.color = color;
        this.status = status;
    }
    Car.prototype = {
        run: function () {
            this.status = 'run';
            console.log(this.status)
        },
        stop: function () {
            this.status = 'stop';
            console.log(this.status)
        },
        gerStatus: function () {
            console.log(this.status)
        }

    }
    var p1 = new Car('ny')
    p1.run()
    p1.stop()
    p1.gerStatus()

问题6: 创建一个 GoTop 对象,当 new 一个 GotTop 对象则会在页面上创建一个回到顶部的元素,点击页面滚动到顶部。拥有以下属性和方法

  1. ct属性,GoTop 对应的 DOM 元素的容器
  2. target属性, GoTop 对应的 DOM 元素
  3. bindEvent 方法, 用于绑定事件
  4. createNode 方法, 用于在容器内创建节点

效果预览 源码

function GoTop($ct) {
        this.ct = $ct;
        this.target = $('<button class="btn">Go Top</button>');
        this.target.css({'padding': '20px'});
        this.bindEvent();
        this.createNode()
    }
    GoTop.prototype = {
        bindEvent: function () {
            this.target.on('click',function (){
                $('body').animate({ scrollTop: 0 }, 200);
            });
        },
        createNode: function () {
            this.ct.append(this.target);
        }
    }

    var run = new GoTop($('.ct'))

问题7: 使用木桶布局实现一个图片墙

效果预览

nanyang24 avatar Aug 18 '17 14:08 nanyang24