閉包復習
- 閉包概念:
封閉空間,包裹 - 閉包的作用:
延長變量的聲明周期
保護內部的數據,代碼更安全
為我們提供一種間接訪問函數內部私有數據的方法 - 函數內部私有數據的訪問
函數內部聲明的變量在函數外部無法訪問
但是有的時候,我們有訪問(讀取數據 + 修改數據)函數內部私有數據的需求 - 在函數內部直接返回數據
- return
只能獲取值,無法修改值
這種方法獲得的數據是一次性的數據,多次獲取得到的數據不是同一份 - 閉包(全世界所有描述 js 的技術文章 32% 在討論閉包)
閉包是持有了本來不屬于自己數據的函數
<script>
function demo() {
var a = 10;
return a;
}
var x = demo();
var y = demo();
console.log(x,y);
</script>
<script>
function demo() {
var obj = {name:"張三"};
return obj;
}
var o1 = demo();
var o2 = demo();
console.log(o1 == o2);
</script>
閉包的基本寫法
<script>
function demo() {
var n = 10;
var func = function () {
return n;
}
return func;
}
var f1 = demo();
var x = f1()
var y = f1();
</script>
<script>
function demo() {
var n = {};
var func = function () {
return n;
}
return func;
}
var f1 = demo();
var x = f1()
var y = f1();
console.log(x == y); //true
</script>
# 閉包的寫法01
<script>
function demo() {
var n = {};
var func = function () {
return n;
}
return func;
}
</script>
# 閉包的寫法02
<script>
function demo() {
var n = {};
return function () {
return n;
};
}
var foo = demo();
console.log(foo());
</script>
# 閉包的寫法03
<script>
var foo = (function() {
var n = {};
return function () {
return n;
};
})();
var obj1 = foo();
var obj2 = foo();
console.log(obj1 == obj2);
</script>
# 訪問私有數據(讀寫)
<script>
function demo() {
var a = 10;
return function (value) {
if (value != undefined)
{
a = value;
}
return a;
}
}
var func = demo();
console.log(func(20));
console.log(func("這是一個字符串"));
console.log(func());
console.log(func(1));
console.log(func());
</script>
# 返回多個數據 方式一
<script>
function person() {
var name = "張三";
var age = 20;
return function () {
return [name,age];
}
}
var foo = person();
console.log(foo()[0]);
console.log(foo()[1]);
</script>
# 返回多個數據 方式二
<script>
function person() {
var name = "張三";
var age = 20;
return [function () {
return name;
},function () {
return age;
}]
}
var foo = person();
console.log(foo[0]());
console.log(foo[1]());
</script>
# 返回多個數據 方式三
<script>
function person() {
var name = "張三";
var age = 20;
return {
getName:function () {
return name;
},
getAge:function () {
return age;
}
}
}
var foo = person();
console.log(foo.getName());
console.log(foo.getAge());
</script>
筆試題練習
<script>
getName(); //3
function getName() {
console.log("1");
}
getName(); //3
function Foo() {
this.getName = function () {
console.log("2");
}
}
function getName() {
console.log("3");
}
new Foo().getName();
</script>
<script>
f2(); //報錯
f1();
f2(); //哈哈
function f1() {
function f2() {
console.log("f2");
}
}
function f2() {
console.log("哈哈");
}
</script>
閉包獲取和設置數據
<script>
function demo() {
var name = "變形記";
var author = "卡夫卡";
var price = 12.23;
var des = "我是一本書";
function getDes() {
return des;
}
function setDes(value) {
des = value;
}
return {
getName:function () {
return name;
},
getAuthor:function () {
return author;
},
getPrice:function () {
return price;
},
setName:function (value) {
if (value != undefined)
{
name = value;
}
},
setAuthor:function (value) {
if (value != undefined)
{
author = value;
}
},
setPrice:function (value) {
if (value != undefined)
{
price = value;
}
},
getDes:getDes,
setDes:setDes
}
}
var func = demo();
console.log(func.getName());
console.log(func.getAuthor());
console.log(func.getPrice());
// console.log(func.getName("城堡")); //設置
// console.log(func.getName());
func.setName("城堡");
console.log(func.getName());
func.setPrice(30.00);
console.log(func.getPrice());
console.log(func.getDes());
</script>
閉包的作用
- 延長變量的聲明周期
- 保護內部的數據,代碼更安全,做一些數據檢驗處理
- 為我們提供一種間接訪問函數內部私有數據的方法
<script>
var foo = (function () {
var name = "張三";
var age = 20;
return {
getName:function () {
return name;
},
getAge:function () {
return age;
},
setName:function (value) {
name = value;
},
setAge:function (value) {
//檢驗
if (value < 0)
{
value = 0;
}
age = value
}
}
})();
console.log(foo.getName());
foo.setName("李四");
console.log(foo.getName());
// var name = "sss";
// var name = "123";
console.log(foo.getAge());
foo.setAge(-2);
console.log(foo.getAge());
</script>
<script>
function demo() {
var a = 10;
}
demo();
demo();
</script>
setTimeOut 和閉包的執行
- 定時器
setTimeOut(只執行一次)
setInterval(每個一段時間執行一次)
<script>
var date = new Date();
console.log(date);
setTimeout(function () {
console.log("定時器" + new Date);
},2000)
</script>
思考
js 代碼的執行頁面渲染任務
js 主要代碼 (for,語句)
事件型的任務(點擊事件,定時器事件)
js 本身是單線程的
進程:系統中正在運行的運行程序,是 cpu 調度的單位,是分配資源的最小單位
線程:進程中真正執行任務的部分,是 CPU 調度的最小單位, CPU 不會為線程分配資源
關系:包含關系(進程包含線程),一個進程可以擁有多個線程,至少要有一條線程(主線程 - UI 線程)
進程(正在運行的學校)
線程(老師)
線程執行任務的時候是串行執行:一條線程在執行多個任務的時候是按照任務的固定順序一個接著一個的執行
多線程:一個進程可以擁有多個線程,多條線程在執行任務的時候并發(行)執行
多線程的并發執行:多條線程執行多個任務,多個任務一起執行,給人感覺在同時執行
并發和秉性的區別
并發:多個任務同時執行(現象)
并行:能力
for (var i = 0; i < 10; i++) {
// setTimeout((function (j) {
// return function () {
// console.log("定時器" + new Date +j );
// }
// })(i),0);
(function (j) {
setTimeout(function () {
console.log("定時器" + new Date +j );
},0);
})(i);
}
div 事件和閉包
<body>
<div>第1個標簽</div>
<div>第2個標簽</div>
<div>第3個標簽</div>
<div>第4個標簽</div>
<div>第5個標簽</div>
<div>第6個標簽</div>
<script>
//01 獲得頁面中所有的div標簽
var divs = document.getElementsByTagName("div");
for (var i = 0; i < divs.length; i++) {
// divs[i].onclick = (function (j) {
// return function () {
// alert("第" + j + "個標簽");
// }
// })(i);
(function (j) {
divs[j].onclick = function () {
alert("第" + j + "個標簽");
}
})(i)
}
</script>
</body>
函數補充說明
- 函數的幾種創建方式
- 函數聲明 function 名稱(參數1,參數2){函數體}
- 函數表達式
var 變量1 = functon 名稱(參數1,參數2){函數體} 命名函數表達式
var 變量2 = function(參數1,參數2){函數體} 匿名函數表達式 - 使用構造函數來創建
var func = new Function()
var func01 = function name() {
console.log("func01");
}
var func02 = function () {
console.log("func02");
}
//細微的差別:name(函數名稱)
//函數本身是對象,函數有屬性(name)
console.log(func01.name); //name
console.log(func02.name); //空 谷歌瀏覽器打印的結果是func02
func01.des = "des";
console.log(func01.des);
var obj = {
name:"張三",
showName:function () {
console.log(this.name);
}
}
delete obj.showName;
obj.showName();
// function demo() {
// var a = 10;
// var b = 20;
// }
//
//
// {
// var a = 10;
// var b = 20;
// }
//
// 函數:函數是有名字的代碼塊。
// demo();
// demo();
// demo();
函數的回調
- 函數回調:函數作為其他函數的參數來使用。
- 需要注意 this 的指向
function foo(callBack,callBackObj) {
//............
if (typeof callBack == "string")
{
callBack = callBackObj[callBack]
}
callBack.call(callBackObj); //以普通函數方式調用 this(window)
}
function demo() {
console.log("demo");
}
foo(demo);
var dog = {
color:"紅色",
age:0.3,
showAge:function () {
console.log(this.age);
}
}
foo(dog.showAge,dog);
var person = {
color:"紅色",
age:99,
showAge:function () {
console.log(this.age);
}
}
foo(person.showAge,person);
foo("showAge",person); //兼容性處理
函數作為返回值(計數器)
function demo() {
var count = 0;
return function () {
return count ++;
}
}
var next = demo();
console.log(next()); //0
console.log(next());
console.log(next());
console.log(next());
console.log(next());
自定義函數(惰性函數定義)
- 自定義函數(惰性函數定義)
特點:函數的內容在第二次調用的時候才被正確的定義,第一次調用的時候主要做一些初始化處理 + 更新函數 - 使用的場景:
代碼需要做一些一次性的初始化處理
function foo() {
console.log("foo!");
//初始化操作......
foo = function () {
console.log("foo! foo!");
foo = function () {
console.log("foo! foo! foo!");
}
}
}
foo(); // foo!
foo(); // foo! foo!
foo(); // foo! foo! foo!
- 使用注意:
- 添加在函數對象上面的成員會丟失
- 如果把函數賦值給變量或者是稱為對象的方法,那么再調用的時候還是執行舊的函數體
function demo() {
console.log("1");
demo = function () {
console.log("2");
}
}
demo.des = "描述信息";
console.log(demo.des);
// demo();
console.log(demo.des);
//01 賦值給變量
// var func = demo;
// func(); //1
// func(); //1
// func(); //1
// demo(); //2
//01 賦值給對象
var o = {};
o.desLog = demo;
o.desLog();
o.desLog();
o.desLog();
demo();
即時對象初始化
({
name:"張安",
age:23,
showName:function () {
console.log(this.name);
},
showAge:function () {
console.log(this.age);
},
init:function (nameValue,ageValue) {
//需要進行初始化處理的代碼
//...............
this.name = nameValue;
this.age = ageValue;
this.showName();
this.showAge();
}
}).init("隔壁老王","23");
即時函數補充
- 即時函數的組成
- 函數 function (形參){函數體}
- ()把函數包裹起來
- ()馬上調用并執行函數
//01
(function () {
console.log("我要過節啦,好開心啊");
})();
//02
(function () {
console.log("我要過節啦,好開心啊");
}());
//03 非主流的寫法
!function () {
console.log("我要過節啦,好開心啊");
}();
+function () {
console.log("我要過節啦,好開心啊");
}();
-function () {
console.log("我要過節啦,好開心啊");
}();
~function () {
console.log("我要過節啦,好開心啊");
}();
- 即時函數可以接收參數也可以有返回值
var a = 10;
var result = (function (n) {
return n + 1;
})(a);
console.log(result);
筆試題練習
- 穿件對象的幾種方法
- 字面量
- 內置構造函數
- 工廠函數
- 自定義構造函數
# 01 以下創建對象的方法,錯誤的是:
<script>
var obj1 = new Object();
obj1.name = "XMG";
obj1.getName = function () {
return this.name;
}
console.log(obj1.getName());
</script>
<script>
var obj2 = {
name:"XMG",
getName:function () {
return this.name;
}
}
console.log(obj2.getName());
</script>
<script>
var MYClass = function () {
this.name = "XMG";
this.getName = function () {
return this.name;
}
};
var obj3 = new MYClass();
console.log(obj3.getName());
</script>
<!--<script>-->
<!--var obj4; //undefined-->
<!--obj4.name = "XMG";-->
<!--obj4.getName = function () {-->
<!--return this.name;-->
<!--};-->
<!--console.log(obj4);-->
<!--</script>-->
函數和變量聲明的提升(函數和變量同名)
# 02 請給出以下代碼的打印結果
<script>
console.log(test); //函數
function test() {};
console.log(typeof test); //function?string
var test = "2017";
console.log(test); //2017
</script>
# 03 請給出以下代碼的輸出結果
<!--<script>-->
<!--var f = new Number(true); //僅僅為1的時候|true-->
<!--if (f == true) {-->
<!--var a = 10;-->
<!--}-->
<!--function fn() {-->
<!--var b = 20;-->
<!--c = 30;-->
<!--}-->
<!--fn();-->
<!--// console.log(a); //? 10 ? undefined ?報錯-->
<!--// console.log(b); //? 報錯-->
<!--console.log(c); //30-->
<!--</script>-->
<!--# 04 請給出下面代碼的輸出結果-->
<!--<script>-->
<!--var str1 = new String("demo01");-->
<!--var str2 = new String("demo01");-->
<!--var str3 = "demo01";-->
<!--console.log("undefined" == undefined); //不相等-->
<!--console.log("+++");-->
<!--console.log(str1 == str2); //false-->
<!--console.log(str1 == str3); //true-->
<!--var name = 'World!';-->
<!--(function () {-->
<!--//"undefined" == undefined 相等比較(如果類型不一樣那么會隱式轉換)-->
<!--if (typeof name == undefined) {-->
<!--var name = '文頂頂';-->
<!--console.log('Goodbye ' + name);-->
<!--} else {-->
<!--console.log('Hello ' + name);-->
<!--}-->
<!--})();-->
<!--</script>-->
<!--#05 請給出下面代碼的結果-->
<!--<script>-->
<!--console.log(Array.isArray(Array.prototype));-->
<!--</script>-->
#06 請給出下面代碼的輸出結果
<!--<script>-->
<!--//[0] == true false-->
<!--//[1] == true true-->
<!--//數組 在比較的時候只有內容是1的時候為true-->
<!--//if判斷的時候 [0] [1] [] {}都為真 -->
<!--// 對象和布爾類型(true|false)比較永遠都為false-->
<!--//幾種結果:true false NO-->
<!--var a = [];-->
<!--if (a) {-->
<!--console.log([1] == true); //false-->
<!--} else {-->
<!--console.log("NO");-->
<!--}-->
<!--console.log("___");-->
<!--</script>-->
#07 請給出下面代碼的輸出結果
<script>
(function(){
var x = y = 1;
//y = 1;
//var x = 1;
})();
console.log(y); //能夠獲取y的值嗎?
console.log(x); //能夠獲取x的值嗎?
筆試題2
- 考察
prototype 和proto
構造函數.prototype
對象.proto
注意:
對象.prototype 和 對象.proto_ 不是一回事
對象.prototype 指的是訪問對象的 prototype 屬性
如果該對象是一個普通的對象(非構造函數),那么不存在該屬性
對象.proto 指的是訪問創建該對象的構造函數的原型對象,等價于 構造函數.prototype
# 01 請給出下面代碼的輸出
<script>
var a = {};
var b = Object.prototype;
console.log(a.__proto__ == Object.prototype); //true
console.log(a.prototype === b); //false
console.log(Object.getPrototypeOf(a) === b); //true ? false
</script>
#02 請給出下面代碼的輸出結果
<!--<script>-->
<!--function f() {}-->
<!--var a = f.prototype;-->
<!--var b = Object.getPrototypeOf(f); //f.__proto__ = Function.prototype = 空函數-->
<!--console.log(a === b);-->
<!--console.log("++++");-->
<!--// console.log(a,b);-->
<!--console.log(b == f);-->
<!--</script>-->
<!--#03 請給出輸出結果-->
<script>
function foo() { }
var oldName = foo.name;
foo.name = "bar"; //函數設置name屬性沒有作用
console.log(oldName); //undefined ==》foo
console.log(foo.name); //bar ==> foo
console.log(oldName === foo.name);
// console.log([oldName, foo.name]);
</script>
#04 請給出輸出結果
<script>
function f() {}
var parent = Object.getPrototypeOf(f); //f.__proto__ =》Function.prototype (空函數)
console.log(f.name); // ?
console.log(parent.name); // ?
//A "f", "Empty" 正確
//B "f", undefined
設計模式的簡單說明
- 設計模式
解決軟件開發中的常見應用場景所采取的不同套路
常見的設計模式:23種 - 分類
創建型的模式
行為型的模式
常用:單利模式 + 工廠模式 + 觀察者模式 + 策略模式 + 代理模式 + 享元模式 + 橋接模式
推薦:大話設計模式(大話數據結構)
其主要是架構師工作 - 起源
建筑行業(哥特風格 中式風格)
Gof 四人組
工廠函數創建對象
function createPerson(name,age) {
var o = new Object();
o.name = name;
o.age = age;
return o;
}
var p1 = createPerson("張三",20);
工廠函數簡單說明
- 工廠模式
核心過程 - 提供一個父構造函數
- 設置父構造函數的原型對象(方法)
- 在父構造函數身上添加靜態工廠方法
- 定制合作伙伴(創建子構造函數)
- 使用工廠函數來創建對象
//01 提供一個父構造函數
function MakePhone() {}
//02 設置父構造函數的原型對象(方法)
MakePhone.prototype.desLog = function () {
console.log("我們的口號是:" + this.des);
}
//03 在父構造函數身上添加靜態的工廠方法
MakePhone.factory = function (type) {
//001 接受參數,并做容錯性處理
var typeString = type;
if (typeString == undefined) {
throw "請選擇廠商";
}
//002 判斷是否支持生產
if (typeof MakePhone[typeString] != "function") {
throw "不支持該手機廠商!";
}
//MakePhone.oppo ==》當做構造函數來使用
//003 設置繼承
MakePhone[typeString].prototype = MakePhone.prototype; //獲得原型對象上面的方法
//004 創建產品
var newPhone = new MakePhone[typeString]();
return newPhone;
}
//04 定制合作伙伴(設置子構造函數)
MakePhone.oppo = function () {
this.des = "充電兩小時,通話五分鐘";
}
MakePhone.vivo = function () {
this.des = "照亮你的美,啊哈哈哈哈";
}
MakePhone.zuimei = function () {
this.des = "你的青春我做主";
}
// 05 使用工廠函數來創建對象
var oppo = MakePhone.factory("oppo");
oppo.desLog();
var vivo = MakePhone.factory("vivo");
vivo.desLog();
var zuimei = MakePhone.factory("zuimei");
zuimei.desLog();