JS 原型的妙用
实例和对象的区别
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 原型的妙用的更多相关文章
- JS原型链
JS作为发展了多年了对象语言,支持继承,和完全面向对象语言不同的是,JS依赖原型链来实现对象的继承. 首先JS的对象分两大类,函数对象和普通对象,每个对象均内置__proto__属性,在不人为赋值__ ...
- 深入分析JS原型链以及为什么不能在原型链上使用对象
在刚刚接触JS原型链的时候都会接触到一个熟悉的名词:prototype:如果你曾经深入过prototype,你会接触到另一个名词:__proto__(注意:两边各有两条下划线,不是一条).以下将会围绕 ...
- 【09-23】js原型继承学习笔记
js原型继承学习笔记 function funcA(){ this.a="prototype a"; } var b=new funcA(); b.a="object a ...
- js原型
1.js基本类型和对象类型 js的简单类型包括数字(其中NaN为数字类型).字符串(类似'A'为字符,js没字符类型).布尔值.null值和undefined值.其他所有的值都是对象.数字.字符串和布 ...
- js原型链与继承(初体验)
js原型链与继承是js中的重点,所以我们通过以下三个例子来进行详细的讲解. 首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询 ...
- JS 原型链图形详解
JS原型链 这篇文章是「深入ECMA-262-3」系列的一个概览和摘要.每个部分都包含了对应章节的链接,所以你可以阅读它们以便对其有更深的理解. 对象 ECMAScript做为一个高度抽象的面向对象语 ...
- js原型解析
我们都知道javascript因为具有了继承以及变量等等一系列的特性之后才被人们认为具有一门编程语言的资格,在后续的不断发展中,js在原生的基础上扩展了基于jquery等等的库,甚至衍生了像node. ...
- 深入理解JS原型链与继承
我 觉得阅读精彩的文章是提升自己最快的方法,而且我发现人在不同阶段看待同样的东西都会有不同的收获,有一天你看到一本好书或者好的文章,请记得收藏起来, 隔断时间再去看看,我想应该会有很大的收获.其实今天 ...
- 学习zepto.js(原型方法)
学习zepto.js(原型方法)[1] 转载 新的一周,新的开始,今天来学习一下zepto里边的原型方法,就是通过$.进行调用的方法,也是可以通过$.fn进行扩展的方法: $.camelCase(): ...
随机推荐
- 【hive】——Hive初始了解
1.没有接触,不知道这个事物是什么,所以不会产生任何问题.2.接触了,但是不知道他是什么,反正我每天都在用.3.有一定的了解,不够透彻.那么hive,1.我们对它了解多少?2.它到底是什么?3.hiv ...
- PHP的openssl加密扩展使用小结
h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...
- 使用FileZilla等软件搭建ftp服务器
FTP的全称是File Transfer Protocol(文件传输协议).顾名思义,就是专门用来传输文件的协议. FTP服务器,则是在互联网上提供存储空间的计算机,它们依照FTP协议提供服务.简单地 ...
- [django]l利用xlrd实现xls文件导入数据
代码: #coding:utf-8 import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "www.sett ...
- [WPF系列]基础Combox
示例 参考 WPF combobox SelectedValue binding to string Confused with wpf ComboBox DisplayMemberPath, ...
- 【2016-11-10】【坚持学习】【Day23】【Socket 编程初步了解】
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.
- 嵌入式Linux驱动学习之路(十五)按键驱动-定时器防抖
在之前的定时器驱动程序中,我们发现在连续按下按键的时候,正常情况下应该是一次按下对应一次松开.而程序有时候会显示是两次按下,一次松开.这个问题是因为在按下的时候,因为是机械按键,所以电压信号会产生一定 ...
- jsoup开发网页客户端3
这个系列好久没更新,最近好忙,老大说未来是Html5的,所以最近一直学习前端以及Html5的一些东西.Android5.0的诞生,让我们眼前一亮,独特的Material风格更是吸引了无数人. 话说不学 ...
- 2016-2017-2 《Java程序设计》预备作业1 总结
2016-2017-2 <Java程序设计>预备作业1 总结 预备作业01:你期望的师生关系是什么见https://edu.cnblogs.com/campus/besti/2016-20 ...
- Caliburn.Micro学习笔记(一)----引导类和命名匹配规则
Caliburn.Micro学习笔记目录 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详 ...