一、问答
(一)、写出 构造函数模式、混合模式、模块模式、工厂模式、单例模式、发布订阅模式的范例。
//单例模式范例
var Car = (function(){
var instance;
function init() {
//私有的变量和函数
var speed = 0;
return {
//定义公共的属性和方法
getSpeed: function(){
return speed;
},
setSpeed: function( s ){
speed = s;
}
};
}
return {
getInstance: function() {
if (!instance) {
instance = init();
}
return instance;
}
};
}());
var car = Car.getInstance();
var car2 = Car.getInstance();
car === car2;
1、工厂模式:
//工厂模式 ---通过函数创造一个对象 将其return出来
function createPeople(age,name,sex) {
var o= new Object();
o.age=age;
o.name=name;
o.sex=sex;
return o;
}
var p1=createPeople(18,"zhangsan","M"),
p2=createPeople(20,"lisi","F");
console.log(p1,p2)
2、构造函数模式:
function People(age,name) {
this.age=age;
this.name=name;
this.sayName=function () {
console.log("I am",this.name)
}
}
var p1=new People("1","gouzao");
3、混合模式
function People(name,age) {
this.name=name;
this.age=age;
}
People.prototype.sayAge=function () {
console.log("my age is",this.age)
};
function Students(name,age,teacherName) {
this.teacherName=teacherName;
People.call(this,name,age);
}
Students.prototype=Object.create(People.prototype);
Students.prototype.constructor=Students;
Students.prototype.sayHi=function () {
console.log("Hi! I am student and my name is"+this.name)
};
var student1= new Students("混合",11,"teache11"),
student2= new Students("混合2",22,"teache22");
4、模块模式:
var People=(function () {
var name="laomingzi";
return{
changeName: function (newName) {
name=newName;
},
sayName:function () {
console.log(name)
}
}
}());
People.sayName();
People.changeName("xinmingzi");
People.sayName();
// 常见形式
var Car=(function () {
var carName="奔驰";
function sayCarName() {
console.log(carName)
}
return{
sayNameFunc:sayCarName
}
}());
Car.sayNameFunc()
模块模式相对其他模式来说,安全性会有所提升,例如上面的例子,我们不能够在外面直接访问carName这个变量;
模块模式其实使得js具有一定的封装功能;
5、单例模式:
var Singleton = (function () {
var instantiated;
function init() {
/*这里定义单例代码*/
return {
publicMethod: function () {
console.log('hello world');
},
publicProperty: 'test'
};
}
return {
getInstance: function () {
if (!instantiated) {
instantiated = init();
}
return instantiated;
}
};
})();
/*调用公有的方法来获取实例:*/
Singleton.getInstance().publicMethod();
// var danli=(function() {
// var instant;
// function init() {
// return {
// publicMethod: function(){
// console.log("我是单例模式")
// },
// publicOtherProto:"我是单例模式的OtherProto"
// }
// }
// return{
// getInstance:function () {
// if (!instant){
// instant=init();
// }
// return instant;
// }
// }
//
// }());
// danli.getInstance().publicMethod();
再举个单例模式例子:
var SingletonTester = (function () {
//参数:传递给单例的一个参数集合
function Singleton(args) {
//设置args变量为接收的参数或者为空(如果没有提供的话)
var args = args || {};
//设置name参数
this.name = 'SingletonTester';
//设置pointX的值
this.pointX = args.pointX || 6; //从接收的参数里获取,或者设置为默认值
//设置pointY的值
this.pointY = args.pointY || 10;
}
//实例容器
var instance;
var _static = {
name: 'SingletonTester',
//获取实例的方法
//返回Singleton的实例
getInstance: function (args) {
if (instance === undefined) {
instance = new Singleton(args);
}
return instance;
}
};
return _static;
})();
var singletonTest = SingletonTester.getInstance({ pointX: 5 });
console.log(singletonTest.pointX); // 输出 5
其实单例一般是用在系统间各种模式的通信协调上。
6、发布订阅模式
//无传参模式
EventCenter=(function () {
var events={};
return{
on:function (evt,handle) {
events[evt]=events[evt]||[];
events[evt].push(handle);
},
fire:function (evt) {
if (!events[evt]){ console.log("未绑定此事件"); return}
for(var i=0;i<events[evt].length;i++){
events[evt][i]()
}
},
unbind:function (evt) {
delete events[evt];
}
}
}())
//有参数模式
EventCenter=(function () {
var events={};
return{
on:function (evt,handle) {
events[evt]=events[evt]||[];
events[evt].push({handle:handle});
},
fire:function (evt,arg) {
if (!events[evt]){ console.log("未绑定此事件"); return}
for(var i=0;i<events[evt].length;i++){
events[evt][i].handle(arg)
}
},
unbind:function (evt) {
delete events[evt];
}
}
}());
(二)、使用发布订阅模式写一个事件管理器,可以实现如下方式调用
EventManager.on('text:change', function(val){
console.log('text:change... now val is ' + val);
});
EventManager.fire('text:change', '饥人谷');
EventManager.off('text:change');
EventManager=(function () {
var events={};
return{
on:function (evt,handle) {
events[evt]=events[evt]||[];
events[evt].push(handle); //{事件1:[函数1,函数2]}
},
fire:function (evt) {
if (!events[evt]){ console.log("未绑定此事件"); return}
for(var i=0;i<events[evt].length;i++){
events[evt][i]([].slice.call(arguments,1))
}
},
off:function (evt) {
delete events[evt];
}
}
}());
运行结果:
Paste_Image.png二、代码
(一)、写一个函数createTimer,用于创建计时器,创建的计时器可分别进行计时「新增」。
ps: 1. 计时器内部写法参考前面的任务中Runtime的写法; 2. 体会工厂模式的用法
function createTimer(){
//todo..
}
var timer1 = createTimer();
var timer2 = createTimer();
timer1.start();
for(var i=0;i<100;i++){
console.log(i);
}
timer2.start();
for(var i=0;i<100;i++){
console.log(i);
}
timer1.end();
timer2.end();
console.log(timer1.get());
console.log(timer2.get());
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
function createTimer(){
var startTime,
endTime,
difference;
var timer={
start: function () {
startTime=Date.now();
return startTime
},
end: function () {
endTime=Date.now();
return endTime
},
get:function () {
difference=endTime-startTime;
return difference
}
};
return timer
}
var timer1 = createTimer();
var timer2 = createTimer();
timer1.start();
for(var i=0;i<100;i++){
console.log(i);
}
timer1.end();
alert("timer1的执行时间是"+timer1.get());
timer2.start();
for(var i=0;i<1000;i++){
console.log(i);
}
timer2.end();
alert("timer2的执行时间是"+timer2.get());
</script>
</body>
</html>
预览地址: http://book.jirengu.com/jirengu-inc/jrg-renwu5/%E6%9D%8E%E5%BD%A9/renwu40/renwu40-1.html
(二)、封装一个曝光加载组件,能实现如下方式调用
//$target 是 jquery 对象
// 当窗口滚动时,如果$target 出现在可见区域,执行回调函数里面的代码,且在回调函数内,$(this)代表$target
Expouse.bind($target, function(){
console.log($(this)); // $target
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
<style>
li{
height: 30px;
background-color: aqua;
}
</style>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li class="target"> i am target</li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
$(window).on("scroll",function () {
var $target=$(".target");
function isVisible($node) {
var scrollH=$(window).scrollTop(),
$nodeOffsetH=$node.offset().top,
$nodeHeight=$node.outerHeight(true),
windowH=$(window).height();
if ((scrollH+windowH)>$nodeOffsetH &&(scrollH+windowH)<($nodeHeight+$nodeOffsetH) ){
return true
}else {return false}
}
Expouse=(function(){
function bind(evt,callback) {
callback.call(evt)
}
return {
bind:bind
}
}());
if (isVisible($target)){
Expouse.bind($target, function(){
console.log($(this)); // $target
});
}
});
</script>
</body>
</html>
预览地址: http://book.jirengu.com/jirengu-inc/jrg-renwu5/%E6%9D%8E%E5%BD%A9/renwu40/renwu40-2A.html
// 当窗口滚动时,如果$target 出现在可见区域,执行回调函数里面的代码,且在回调函数内,$(this)代表$target。 仅执行一次回调函数,下次$target 曝光不再执行
Expourse.one($target, function(){
console.log($(this)); // $target
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
<style>
li{
height: 30px;
background-color: aqua;
}
</style>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li class="target"> i am target</li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script>
// //$target 是 jquery 对象
// // 当窗口滚动时,如果$target 出现在可见区域,执行回调函数里面的代码,且在回调函数内,$(this)代表$target
// Expouse.bind($target, function(){
// console.log($(this)); // $target
// });
$(window).on("scroll",function () {
var $target=$(".target");
if ($target.data("isrun")){
return
}
function isVisible($node) {
var scrollH=$(window).scrollTop(),
$nodeOffsetH=$node.offset().top,
$nodeHeight=$node.outerHeight(true),
windowH=$(window).height();
if ((scrollH+windowH)>$nodeOffsetH &&(scrollH+windowH)<($nodeHeight+$nodeOffsetH) ){
return true
}else {return false}
}
Expouse=(function(){
function bind(evt,callback) {
callback.call(evt)
}
return {
bind:bind
}
}());
if (isVisible($target)){
Expouse.bind($target, function(){
console.log($(this)); // $target
});
$target.data("isrun",true)
}
});
</script>
</body>
</html>
预览地址:http://book.jirengu.com/jirengu-inc/jrg-renwu5/%E6%9D%8E%E5%BD%A9/renwu40/renwu40-2B.html
(三)、封装一个 轮播插件,分别使用对象方式和 jquery插件方式来调用
// 要求:html 里有多个carousel,当调用时启动全部的 carousel
//方式1
//通过插件的方式启动所有轮播
$('.carousel').carousel();
//方式2
//通过创建对象的方式启动所有轮播
$('.carousel').each(function(){
new Carousel($(this));
});
a、 插件方法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>27-1无限轮播</title>
<style>
ul,li{
list-style: none;
}
*{
margin: 0;
padding: 0;
}
.ct-img>li{
float: left;
width: 310px;
height: 210px;
display: none;
}
li img{
width: 310px;
height: 210px;
}
.clearfix:after{
content: "";
display: block;
clear: both;
}
.ct-bottom>li{
border-bottom: 5px solid #555;
float: left;
cursor: pointer;
width: 20px;
margin: 0 5px;
border-radius: 2px;
}
.ct-bottom{
position: absolute;
top: 180px;
left: 95px;
}
.ct{
position: relative;
}
a.change{
text-decoration: none;
font-size: larger;
font-weight: 900;
background-color: #666;
color: #fff;
display: inline-block;
vertical-align: middle;
padding: 15px;
width: 10px;
height: 10px;
line-height: 10px;
border-radius: 25px;
opacity: 0.7;
}
a.pre{
position: absolute;
left: 10px;
top: 80px;
}
a.next{
position: absolute;
left: 250px;
top: 80px;
}
a.change:hover{
background-color: #444;
}
</style>
<script type="text/javascript" src="http://gc.kis.scr.kaspersky-labs.com/09DC0C47-7E28-5643-A354-E60E2E2E8CBA/main.js" charset="UTF-8"></script></head>
<body>
<div id="wrap">
<div class="ct">
<ul class="clearfix ct-img">
<li data-nub="0"><a href="#">![](https://img.haomeiwen.com/i2166980/9c8f261bdec45ee9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="1"><a href="#">![](https://img.haomeiwen.com/i2166980/d3fb17e0327a0cc7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="2"><a href="#">![](https://img.haomeiwen.com/i2166980/e6a8ad9a57c5c17c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="3"><a href="#">![](https://img.haomeiwen.com/i2166980/b7bdbfa69d0ec3c4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
</ul>
<ul class="ct-bottom clearfix">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<a href="####" class="pre change"> < </a>
<a href="####" class="next change"> > </a>
</div>
<div class="ct">
<ul class="clearfix ct-img">
<li data-nub="0"><a href="#">![](https://img.haomeiwen.com/i2166980/e6a8ad9a57c5c17c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="1"><a href="#">![](https://img.haomeiwen.com/i2166980/d3fb17e0327a0cc7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="2"><a href="#">![](https://img.haomeiwen.com/i2166980/9c8f261bdec45ee9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="3"><a href="#">![](https://img.haomeiwen.com/i2166980/b7bdbfa69d0ec3c4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
</ul>
<ul class="ct-bottom clearfix">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<a href="####" class="pre change"> < </a>
<a href="####" class="next change"> > </a>
</div>
<div class="ct">
<ul class="clearfix ct-img">
<li data-nub="0"><a href="#">![](https://img.haomeiwen.com/i2166980/9c8f261bdec45ee9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="1"><a href="#">![](https://img.haomeiwen.com/i2166980/d3fb17e0327a0cc7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="2"><a href="#">![](https://img.haomeiwen.com/i2166980/e6a8ad9a57c5c17c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="3"><a href="#">![](https://img.haomeiwen.com/i2166980/b7bdbfa69d0ec3c4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
</ul>
<ul class="ct-bottom clearfix">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<a href="####" class="pre change"> < </a>
<a href="####" class="next change"> > </a>
</div>
</div>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
/* 自定义jQuery方法(开发jQuery插件常用此方法) 其实 $.fn === $.prototype */
$.fn.allGo = function() {
this.each(function () {
var $me=$(this),
$ctImg=$me.find(".ct-img"),
$ctBottom=$me.find(".ct-bottom"),
$next=$me.find(".next"),
$pre=$me.find(".pre"),
$ctBottomLi=$me.find(".ct-bottom>li"),
clock=false,
cur=0;
show(0);
setInterval(function () {
playNext()
}, 2000);
function show(num) {
if (clock) {
return;
}
clock = true;
$ctBottom.children().eq(num).css("border-bottom-color", "white");
$ctBottom.children().eq(num).siblings().css("border-bottom-color", "#555");
$ctImg.children().eq(num).siblings().css("display", "none");
$ctImg.children().eq(num).fadeOut(500);
$ctImg.children().eq(num).fadeIn(500, function () {
clock = false
});
cur = num;
return cur;
}
function playNext() {
var nextNum = cur + 1;
if (nextNum === 4) {
show(0)
}
else show(nextNum)
}
$next.on("click", function () {
playNext();
});
$pre.on("click", function () {
playPre();
});
function playPre() {
var preNum = cur - 1;
if (preNum === -1) {
show(3)
}
else show(preNum)
}
$ctBottomLi.on("click", function () {
var $cur = $(this),
indexNum = $cur.index();
show(indexNum);
});
});
};
// $(".ct").each(function () {
// $(this).allGo();
// });
$('.ct').allGo();
</script>
</body>
</html>
b、创建对象方法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>27-1无限轮播</title>
<style>
ul,li{
list-style: none;
}
*{
margin: 0;
padding: 0;
}
.ct-img>li{
float: left;
width: 310px;
height: 210px;
display: none;
}
li img{
width: 310px;
height: 210px;
}
.clearfix:after{
content: "";
display: block;
clear: both;
}
.ct-bottom>li{
border-bottom: 5px solid #555;
float: left;
cursor: pointer;
width: 20px;
margin: 0 5px;
border-radius: 2px;
}
.ct-bottom{
position: absolute;
top: 180px;
left: 95px;
}
.ct{
position: relative;
}
a.change{
text-decoration: none;
font-size: larger;
font-weight: 900;
background-color: #666;
color: #fff;
display: inline-block;
vertical-align: middle;
padding: 15px;
width: 10px;
height: 10px;
line-height: 10px;
border-radius: 25px;
opacity: 0.7;
}
a.pre{
position: absolute;
left: 10px;
top: 80px;
}
a.next{
position: absolute;
left: 250px;
top: 80px;
}
a.change:hover{
background-color: #444;
}
</style>
<script type="text/javascript" src="http://gc.kis.scr.kaspersky-labs.com/09DC0C47-7E28-5643-A354-E60E2E2E8CBA/main.js" charset="UTF-8"></script></head>
<body>
<div id="wrap">
<div class="ct">
<ul class="clearfix ct-img">
<li data-nub="0"><a href="#">![](https://img.haomeiwen.com/i2166980/9c8f261bdec45ee9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="1"><a href="#">![](https://img.haomeiwen.com/i2166980/d3fb17e0327a0cc7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="2"><a href="#">![](https://img.haomeiwen.com/i2166980/e6a8ad9a57c5c17c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="3"><a href="#">![](https://img.haomeiwen.com/i2166980/b7bdbfa69d0ec3c4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
</ul>
<ul class="ct-bottom clearfix">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<a href="####" class="pre change"> < </a>
<a href="####" class="next change"> > </a>
</div>
<div class="ct">
<ul class="clearfix ct-img">
<li data-nub="0"><a href="#">![](https://img.haomeiwen.com/i2166980/e6a8ad9a57c5c17c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="1"><a href="#">![](https://img.haomeiwen.com/i2166980/d3fb17e0327a0cc7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="2"><a href="#">![](https://img.haomeiwen.com/i2166980/9c8f261bdec45ee9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="3"><a href="#">![](https://img.haomeiwen.com/i2166980/b7bdbfa69d0ec3c4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
</ul>
<ul class="ct-bottom clearfix">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<a href="####" class="pre change"> < </a>
<a href="####" class="next change"> > </a>
</div>
<div class="ct">
<ul class="clearfix ct-img">
<li data-nub="0"><a href="#">![](https://img.haomeiwen.com/i2166980/9c8f261bdec45ee9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="1"><a href="#">![](https://img.haomeiwen.com/i2166980/d3fb17e0327a0cc7.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="2"><a href="#">![](https://img.haomeiwen.com/i2166980/e6a8ad9a57c5c17c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
<li data-nub="3"><a href="#">![](https://img.haomeiwen.com/i2166980/b7bdbfa69d0ec3c4.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)</a></li>
</ul>
<ul class="ct-bottom clearfix">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<a href="####" class="pre change"> < </a>
<a href="####" class="next change"> > </a>
</div>
</div>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function Carousel($node) {
this.$node=$node;
var $me=this.$node,
$ctImg=$me.find(".ct-img"),
$ctBottom=$me.find(".ct-bottom"),
$next=$me.find(".next"),
$pre=$me.find(".pre"),
$ctBottomLi=$me.find(".ct-bottom>li"),
clock=false,
cur=0;
show(0);
setInterval(function () {
playNext()
}, 2000);
function show(num) {
if (clock) {
return;
}
clock = true;
$ctBottom.children().eq(num).css("border-bottom-color", "white");
$ctBottom.children().eq(num).siblings().css("border-bottom-color", "#555");
$ctImg.children().eq(num).siblings().css("display", "none");
$ctImg.children().eq(num).fadeOut(500);
$ctImg.children().eq(num).fadeIn(500, function () {
clock = false
});
cur = num;
return cur;
}
function playNext() {
var nextNum = cur + 1;
if (nextNum === 4) {
show(0)
}
else show(nextNum)
}
$next.on("click", function () {
playNext();
});
$pre.on("click", function () {
playPre();
});
function playPre() {
var preNum = cur - 1;
if (preNum === -1) {
show(3)
}
else show(preNum)
}
$ctBottomLi.on("click", function () {
var $cur = $(this),
indexNum = $cur.index();
show(indexNum);
});
}
$('.ct').each(function(){
new Carousel($(this));
})
</script>
</body>
</html>
另:JavaScript模式相关书籍
1、JavaScript模式 https://book.douban.com/subject/11506062/
2、JavaScript设计模式 https://book.douban.com/subject/3329540/
3、JavaScript设计模式与开发实践 https://book.douban.com/subject/26382780/
**本文版权归本人即简书笔名:该账户已被查封 所有,如需转载请注明出处。谢谢! *
网友评论