实例和对象的区别

JS语法没有类这个概念(当然ES6引用了类这个概念)。只能通过构造函数来创建类,new一个类之后就是对象。

JS是面向对象的语言,一切都是对象。那么函数也是对象、实例也是对象、对象字面量也是对象,而且JS中所有对象的成员都是公用的。

即:对象是一个具有多种属性的内容结构!所以:实例都是对象,而对象不全是实例!

对象和函数

函数就是对象;对象不一定是函数,而且在JS中,函数还是第一类对象,而且还是也是实例。

var add = new Function('a','b','return a+b');
//add为一个函数,它也是构造函数Function的一个实例,即对象
add(1,2) //

结论:一切皆为对象,函数为第一类对象。而对象不是实例!

什么是继承

继承就是子类化,从一个基础或者超类对象中继承相关的属性

JS继承的方式

JS继承主要有两种、apply(call)和 prototype基础,很多继承的设计模式都是按照这两个继承来设计的。

1.先看apply(call)的一个例子

function Stream(x, y){
this.x = x;
this.y = y;
//私有变量
var _x = 9;
//暴露子类对私有变量的访问
this.getX = function(){
//_x:9
console.log("_x:"+_x);
return _x;
}
}
var stream = function(){
var array = Array.prototype.slice.call(arguments);
//继承Stream中的公有属性。array是对公有属性赋值。
Stream.apply(this,array);
}
var str = new stream(7, 8);
//x:7
console.log("x:" + str.x);
//str:9
console.log("str:" + str.getX());

2.另外一个是原型继承,通过它可以实现类和实例直接的继承关系。

2.1 实现数据备份

// 通过原型来来实现数据备份
function p(x){
this.x = x;
}
p.prototype.backup = function(){ //备份函数、初始化第一个对象时进行备份,还原也只能还原第一个对象的数据
for(i in this){
p.prototype[i] = this[i];
}
}
var p1 = new p(1);
console.log(p1.x); //1;
p1.backup(); //p1的原型对象(构造函数、构造类)的原型属性应用了p1对象。即p.prototype.x = p1.x;
p1.x = 10;
p1 = p.prototype; //p1对象赋值给p.prototype,p1.x = p.prototype.x 实现对象的备份还原。
console.log(p1.x);

2.2  继承封装

   这也是原型最重要的特征,下面是套路,这是一种原型设计模式,需要牢记!

  extend 方法

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属性。(uber是一个德语词,意思是"向上"、"上一层"。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
}
extend(Cat,Animal);
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

method方法

这个例子是来源于《JS模式》这本书

if(!Function.prototype.method){
Function.prototype.method = function(name,fn){
//this是调用method的构造函数
this.prototype[name] = fn;
return this;
}
}
var Person = function(name){
this.name = name;
}.method("setName",function(name){
//this是构造函数的实例(new方法)
this.name = name;
return this;
}).method("getName",function(){
console.log(this.name);
return this;
});
var p = new Person("anthonyliu");
//anthonyliu
p.getName();
p.setName("liuyinlei").getName();
//liuyinlei

 __proto__和prototype

对象不一定有prototype属性,例如{};构造函数肯定有prototype属性,而且构造函数定义的实例会有__proto__属性,

该属性值是构造构造函数的prototype指向的对象,如下:

function A(x,y){
this.x = x;
this.y = y;
}
A.prototype.getX = function(){
return this.x;
}
var a = new A(3,4);
console.log(a.__proto__.constructor); //A
//即:a.__proto__.constructor.prototype = A.prototype;A.prototype.construct = a.__proto__.construct = A

还有更诡异的事情:一个函数的静态成员对象的__proto__赋值一个对象b,那个可以直接通过静态成员访问b对象中的成员:例如:

var request = {
x:"100"
};
function A() {
// body...
}
A.request = {
__proto__:request
};
console.log(A.request.x); //100

express源码中也能看到其中的身影:

function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
//继承事件的方法和proto的方法。
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
//app.request为req(node的http.IncomingMessage 类的一个实例)
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}

其实__proto__属性可以看做是一个mixin方法:

var proto ={};
var mixin = {"x":"y"};
proto.__proto__ = mixin;
console.log(proto.x); //y

console打印原型

var A = function (argument) {
// body...
};
A.prototype.pipe = function(){ };
//A { pipe: [Function] }
console.log(A.prototype);
//打印nodejs中的stream.prototype
var stream = require("stream");
//Stream { pipe: [Function] }
console.log(stream.prototype);

打印函数

function B(x,y){
this.x = x;
this.y = y;
}; var A = function (x,y) {
B.apply(this,arguments);
// body..
};
A.prototype.pipe = function(){ };
A.prototype.pipe2 = function(){ }
A.getA =function(){ }
A.getB = function(){ }
//{ [Function: A]
// getA: [Function],
// getB: [Function]
// }
console.log(A); 

mixin设计模式

可以将继承MiXin看作为一种通过扩展收集功能的方式

e.mixin = function(t) {
for (var i in e.prototype)
t[i] = e.prototype[i]; //调用该模块,t继承了e.prototype的方法,
return t
}
//e.prototype写法是:
function e() {}
var j = e.prototype;
return j.on = function(e, t) {},
j.emit =function(e,t){},
j //引用,e.prototype中的方法都是j的方法,即实现了继承。

下面是一个简单的mixin模式写法

function A(x){
this.x = x;
}
A.prototype.setA = function(){console.log("coming in a");}
A.prototype.setB = function(){console.log("coming in b");}
A.mixin = function(t){
for(key in A.prototype){
t[key] = A.prototype[key];
}
}
var b= {};
A.mixin(b);
b.setA(); //coming in a

小结:mixin设计模式非常像拷贝继承,简单说,把父对象的所有属性和方法,拷贝进子对象,扩展方法,都是从父对象来扩展方法。

在nodejs中util包有个函数inherits,也具有mixin的功能。建议用ES6 的 class 和 extends 关键词获得语言层面的继承支持,记住下面的套路:

const util = require('util');
const EventEmitter = require('events'); function MyStream() {
EventEmitter.call(this);
} util.inherits(MyStream, EventEmitter); MyStream.prototype.write = function(data) {
this.emit('data', data);
}; const stream = new MyStream(); console.log(stream instanceof EventEmitter); // true
console.log(MyStream.super_ === EventEmitter); // true stream.on('data', (data) => {
console.log(`接收的数据:"${data}"`);
});
stream.write('运作良好!'); // 接收的数据:"运作良好!"

JS 原型的妙用的更多相关文章

  1. JS原型链

    JS作为发展了多年了对象语言,支持继承,和完全面向对象语言不同的是,JS依赖原型链来实现对象的继承. 首先JS的对象分两大类,函数对象和普通对象,每个对象均内置__proto__属性,在不人为赋值__ ...

  2. 深入分析JS原型链以及为什么不能在原型链上使用对象

    在刚刚接触JS原型链的时候都会接触到一个熟悉的名词:prototype:如果你曾经深入过prototype,你会接触到另一个名词:__proto__(注意:两边各有两条下划线,不是一条).以下将会围绕 ...

  3. 【09-23】js原型继承学习笔记

    js原型继承学习笔记 function funcA(){ this.a="prototype a"; } var b=new funcA(); b.a="object a ...

  4. js原型

    1.js基本类型和对象类型 js的简单类型包括数字(其中NaN为数字类型).字符串(类似'A'为字符,js没字符类型).布尔值.null值和undefined值.其他所有的值都是对象.数字.字符串和布 ...

  5. js原型链与继承(初体验)

    js原型链与继承是js中的重点,所以我们通过以下三个例子来进行详细的讲解. 首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询 ...

  6. JS 原型链图形详解

    JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语 ...

  7. js原型解析

    我们都知道javascript因为具有了继承以及变量等等一系列的特性之后才被人们认为具有一门编程语言的资格,在后续的不断发展中,js在原生的基础上扩展了基于jquery等等的库,甚至衍生了像node. ...

  8. 深入理解JS原型链与继承

    我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天 ...

  9. 学习zepto.js(原型方法)

    学习zepto.js(原型方法)[1] 转载 新的一周,新的开始,今天来学习一下zepto里边的原型方法,就是通过$.进行调用的方法,也是可以通过$.fn进行扩展的方法: $.camelCase(): ...

随机推荐

  1. C#按位操作,直接操作INT数据类型的某一位

    /// <summary> /// 根据Int类型的值,返回用1或0(对应True或Flase)填充的数组 /// <remarks>从右侧开始向左索引(0~31)</r ...

  2. android 控件在不同状态下的内容样式与背景样式

    1 控件内容(如字体颜色)在不同状态下有不同的表现色ref:http://developer.android.com/guide/topics/resources/color-list-resourc ...

  3. linux mysql-5.6.26 安装

    下载地址 ftp://mirror.switch.ch/mirror/mysql/Downloads/MySQL-5.6/mysql-5.6.26-linux-glibc2.5-x86_64.tar. ...

  4. 最小生成树 kruskal算法 codevs 1638 修复公路

    1638 修复公路  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 钻石 Diamond 题解       题目描述 Description A地区在地震过后,连接所有村庄的公 ...

  5. apache服务器启动时提示httpd: apr_sockaddr_info_get() failed for

    apache服务器启动时提示httpd: apr_sockaddr_info_get() failed for 在RedHat Linux 5 与 CentOS 5服务器上配置好apache后,启动或 ...

  6. apache 多站点配置

    安装时可以选择gbk-gb2312的编码(我自己用utf-8). 2: 修改本机的hosts文件,如下: 示例:127.0.0.1            localhost127.0.0.1      ...

  7. ToolProvider.getSystemJavaCompiler() Return NULL!

    http://www.cnblogs.com/fangwenyu/archive/2011/10/12/2209051.html

  8. redis 学习笔记(1)-编译、启动、停止

    一.下载.编译 redis是以源码方式发行的,先下载源码,然后在linux下编译 1.1 http://www.redis.io/download 先到这里下载Stable稳定版,目前最新版本是2.8 ...

  9. Mongodb学习笔记二(Mongodb基本命令)

    第二章 基本命令 一.Mongodb命令 说明:Mongodb命令是区分大小写的,使用的命名规则是驼峰命名法. 对于database和collection无需主动创建,在插入数据时,如果databas ...

  10. .bat文件和Jar包的生成及运行

    .bat文件和Jar包的生成及运行 1.Jar包简单介绍 Jar包是Java中所特有的一种压缩文档,有点类似于zip包,区别在于Jar包中有一个META-INF\MANIFEST.MF文件(在生成Ja ...