[js高手之路] 设计模式系列课程 - jQuery的链式调用与灵活的构造函数
一、我们从一个简单的构造函数+原型程序开始
var G = function(){};
G.prototype = {
length : 5,
size : function(){
return this.length;
}
}
上例是个非常简单的程序,如果需要调用,我们可以用new的方式
var G = function () {
if( this instanceof G ) {
return this;
}else {
return new G();
}
};
把G的构造函数改造一下,判断this是否是当前G函数的实例,如果是,直接返回,如果不是,返回new G() 这样根据原型对象的查找原则,就能确保调用到size方法
完整的代码:
var G = function () {
if( this instanceof G ) {
return this;
}else {
return new G();
}
};
G.prototype = {
length: 5,
size: function () {
return this.length;
}
}
console.log( G().size() );
在jquery框架中,他是怎么做的?
var G = function () {
return G.fn;
};
G.fn = G.prototype = {
length: 5,
size: function () {
return this.length;
}
}
console.log( G.prototype.size() ); //
console.log( G().size() ); //
在jquery中, 为函数G增加一个属性fn( 记住:在js中函数是对象,这其实就是给对象增加了一个属性 ),然后在G函数中返回 G.fn 就是就是返回G.prototype
那么用G().size 其实就是相当于调用 G.prototype.size()
二、在jquery中,这个构造函数一般是用来选择元素的。
G返回的是G的原型对象,我们要增加选择元素的功能,自然而然,就是往原型对象上添加:
var G = function ( id ) {
return G.fn.init( id );
};
G.fn = G.prototype = {
init : function( id ){
return document.getElementById( id );
},
length: 5,
size: function () {
return this.length;
}
}
向G的原型对象上添加一个init方法, 然后在构造函数中调用
window.onload = function(){
console.log( G( 'box' ) );
G('box').style.backgroundColor = 'red';
// G('box').size(); //报错,无法链式调用
}
<div id="box">ghost wu tell you how to learn design pattern</div>
虽然通过init方法,能够选择到dom元素,但是不能实现链式调用, 因为G('box')返回的是一个dom对象, 而在dom对象上是没有size这个方法的,因为size是G.prototype上的
所以要实现链式调用,就要确保init方法返回的是G的实例或者G.prototype, 这个时候,this就可以派上用场了
<script>
var G = function (id) {
return G.fn.init(id);
};
G.fn = G.prototype = {
init: function (id) {
this[0] = document.getElementById(id);
this.length = 1;
return this;
},
length: 0,
size: function () {
return this.length;
}
}
window.onload = function () {
console.log(G('box'));
console.log( G('box2').size() );
}
</script>
<div id="box">ghost wu tell you how to learn design pattern</div>
<div id="box2">id为box2的第二个div</div>
把选择到的元素放在this中, 这个时候的this指向的是G.fn,G.prototype?
因为在构造函数中,是这样调用的: G.fn.init( id ), 我把G.fn标成红色, 也就是相当于G.fn是一个对象,没错他确实就是一个对象G.prototype,所以在init中的this指向的就是
init方法前面的对象( G.fn, G.prototype ).
三、this覆盖
接下来,就会产生一个问题, this共用之后,元素选择就会产生覆盖
<script>
var G = function (id) {
return G.fn.init(id);
};
G.fn = G.prototype = {
init: function (id) {
this[0] = document.getElementById(id);
this.length = 1;
console.log( this === G.fn, this === G.prototype, this );
return this;
},
length: 0,
size: function () {
return this.length;
}
} window.onload = function(){
console.log( G( 'box' ) );
console.log( G( 'box2' ) );
}
</script> <div id="box">ghost wu tell you how to learn design pattern</div>
<div id="box2">id为box2的第二个div</div>
调用两次构造函数G 去获取元素的时候, this[0] 现在都指向了 id为box2的元素, 把第一次G('box')选择到的id为box的元素覆盖了,产生覆盖的原因是this共用,那么我们
可以通过什么方法把this分开呢?不同的this指向不同的实例? 用什么? 恩,对了, 用new,每次new一个构造函数就会生成新的实例
四、解决this覆盖与链式调用
<script>
var G = function (id) {
return new G.fn.init(id);
};
G.fn = G.prototype = {
init: function (id) {
this[0] = document.getElementById(id);
this.length = 1;
return this;
},
length: 0,
size: function () {
return this.length;
}
}
window.onload = function(){
console.log( G( 'box' ) );
console.log( G( 'box2' ) );
}
</script>
<div id="box">ghost wu tell you how to learn design pattern</div>
<div id="box2">id为box2的第二个div</div>
通过构造函数中new G.fn.init( id ) 的方式,每次生成一个新的实例,但是产生了一个新的问题,不能链式调用, 因为init中的this发生了改变,不再指向( G.fn, G.prototype ).
var G = function (id) {
return new G.fn.init(id);
};
G.fn = G.prototype = {
init: function (id) {
this[0] = document.getElementById(id);
this.length = 1;
return this;
},
length: 0,
size: function () {
return this.length;
}
}
G.fn.init.prototype = G.fn;
加上G.fn.init.prototype = G.fn;我们就修正了this的覆盖与链式调用问题
五、扩展选择器
上面支持id选择器,我们只需要在init函数加上其他类型的扩展就可以了,比如,我这里增加了一个标签选择器
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
div,p {
border:1px solid red;
margin: 10px;
padding: 10px;
}
</style>
<script>
var G = function ( selector, context ) {
return new G.fn.init( selector, context );
};
G.fn = G.prototype = {
constructor : G,
init: function ( selector, context ) {
this.length = 0;
context = context || document;
if ( selector.indexOf( '#' ) == 0 ) {
this[0] = document.getElementById( selector.substring( 1 ) );
this.length = 1;
}else {
var aNode = context.getElementsByTagName( selector );
for( var i = 0, len = aNode.length; i < len; i++ ){
this[i] = aNode[i];
}
this.length = len;
}
this.selector = selector;
this.context = context;
return this;
},
length: 0,
size: function () {
return this.length;
}
}
G.fn.init.prototype = G.fn; window.onload = function(){
console.log( G('#box')[0] );
var aP = G('p', G('#box')[0]);
// var aP = G('p');
// var aP = G('#p1');
for( var i = 0, len = aP.size(); i < len; i++ ){
aP[i].style.backgroundColor = 'blue';
}
}
</script>
</head> <body>
<div id="box">
<p>跟着ghostwu学习设计模式</p>
<p>跟着ghostwu学习设计模式</p>
<p>跟着ghostwu学习设计模式</p>
<p>跟着ghostwu学习设计模式</p>
</div>
<p id="p1">跟着ghostwu学习设计模式</p>
<p>跟着ghostwu学习设计模式</p>
</body> </html>
[js高手之路] 设计模式系列课程 - jQuery的链式调用与灵活的构造函数的更多相关文章
- [js高手之路] 设计模式系列课程 - jQuery的extend插件机制
这里在之前的文章[js高手之路] 设计模式系列课程 - jQuery的链式调用与灵活的构造函数基础上增加一个extend浅拷贝,可以为对象方便的扩展属性和方法, jquery的插件扩展机制,大致就是这 ...
- [js高手之路]设计模式系列课程-发布者,订阅者重构购物车
发布者订阅者模式,是一种很常见的模式,比如: 一.买卖房子 生活中的买房,卖房,中介就构成了一个发布订阅者模式,买房的人,一般需要的是房源,价格,使用面积等信息,他充当了订阅者的角色 中介拿到卖主的房 ...
- [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表
所谓组合模式,就是把一堆结构分解出来,组成在一起,现实中很多这样的例子,如: 1.肯德基套餐就是一种组合模式, 比如鸡腿堡套餐,一般是是由一个鸡腿堡,一包薯条,一杯可乐等组成的 2.组装的台式机同理, ...
- [js高手之路]设计模式系列课程-单例模式实现模态框
什么是单例呢? 单,就是一个的意思.例:就是实例化出来的对象,那合在一起就是保证一个构造函数只能new出一个实例,为什么要学习单例模式呢?或者说单例模式有哪些常见的应用场景.它的使用还是很广泛,比如: ...
- [js高手之路]设计模式系列课程-设计一个模块化扩展功能(define)和使用(use)库
模块化的诞生标志着javascript开发进入工业时代,近几年随着es6, require js( sea js ), node js崛起,特别是es6和node js自带模块加载功能,给大型程序开发 ...
- [js高手之路] 设计模式系列课程 - DOM迭代器(2)
如果你对jquery比较熟悉的话,应该用过 eq, first, last, get, prev, next, siblings等过滤器和方法.本文,我们就用迭代设计模式来封装实现,类似的功能 < ...
- [js高手之路]设计模式系列课程-委托模式实战微博发布功能
在实际开发中,经常需要为Dom元素绑定事件,如果页面上有4个li元素,点击对应的li,弹出对应的li内容,怎么做呢?是不是很简单? 大多数人的做法都是:获取元素,绑定事件 <ul> < ...
- [js高手之路] 设计模式系列课程 - 迭代器(1)
迭代器是指通过一种形式依次遍历数组,对象,或者类数组结构中的每个元素. 常见的有jquery中的each方法, ES5自带的forEach方法. 下面我们就来自定义一个类似jquery或者ES5的迭代 ...
- [js高手之路] es6系列教程 - 迭代器与生成器详解
什么是迭代器? 迭代器是一种特殊对象,这种对象具有以下特点: 1,所有对象都有一个next方法 2,每次调用next方法,都会返回一个对象,该对象包含两个属性,一个是value, 表示下一个将要返回的 ...
随机推荐
- JavaScript用typeof判断变量是数组还是对象,都返回object
在JavaScript中所有数据类型严格意义上都是对象,但实际使用中我们还是有类型之分,如果要判断一个变量是数组还是对象使用typeof搞不定,因为它全都返回object. 使用typeof加leng ...
- iOS事件响应链(Responder Chain)
概述 在iOS中,视图的层级一般都是 父视图->添加各种子视图.这时候某个视图(子视图)上有个按钮,需要我们交互.但是有时候我们会发现无论如何都没有反应.这时候可能就是我们对iOS的事件传递响应 ...
- iOS开发实战-时光记账Demo 网络版
之前写了一个本地数据库版本 戳这里 现在这个就是增加了后台 登录注册页面以及web的上传记录展示页面 含有少量php有兴趣可以看下 另外demo中包括数据库操作.json.网络请求等都没有用到第三方库 ...
- LeetCode-Triangle[dp]
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent n ...
- 初学安卓开发随笔之 Menu、toast 用法、活动的四种启动模式 以及 一个方便的Base活动类使用方法
Toast toast 是安卓系统的一种非常棒的提醒方式 首先定义一个弹出Toast的触发点,比如可以是按钮之类 其中 Toast.LENGTH_SHORT是指显示时长 还有一个内置变量为Toast. ...
- 【CSS】div父容器根据子容器大小自适应
Div即父容器不根据内容自动调节高度,我们看下面的代码: <div id="main"> <div id="content"></ ...
- Orchard 学习
https://github.com/OrchardCMS/Orchard 源码下载 http://www.orchardch.com/ 中文介绍网站
- css简单实现五角星评分、点赞收藏、展示评分(半颗星、1/3颗星)
1.前言 之前做的好几个项目中,都会遇到打分,评分,点赞这样的需求,写了很多次,每次需要再写的时候,就会翻出之前写过的代码,然后copy过来.总觉得这样的话没有进步,没有把知识放进脑袋里,所以,自己花 ...
- C#和NewSQL更配 —— CockroachDB入门(可能是C#下的全网首发)
阅读目录 CockroachDB是什么 环境部署 实战 性能测试 结语 一.CockroachDB是什么 CockroachDB(https://www.cockroachlabs.com)是Goog ...
- LoadRunner入门(二)
三.设计场景 1.打开Controller 2.选择运行的脚本 3.场景设置 3.1修改场景模式 点击scenario-convert- 修改场景百分比模式 (1)Group Name:脚本名称 (2 ...