大家好,下面一段程式碼是 typescript五分鐘教學 的範例程式碼編譯後的 JS。
// 重點在下面
var Student = /** @class */ (function () {
function Student(firstName, middleInitial, lastName) {
this.firstName = firstName;
this.middleInitial = middleInitial;
this.lastName = lastName;
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
return Student;
}());
// 重點在上面
function greeter(person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
var user = new Student("Jane", "M.", "User");
document.body.innerHTML = greeter(user);
想請教一下,
/** @class */
是什麼,為什麼要加這麼多的符號,還有class要加"@"?var Student = function Student(firstName, middleInitial, lastName) {
this.firstName = firstName;
this.middleInitial = middleInitial;
this.lastName = lastName;
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
// 這是你的程式,你已經寫了一個Student了
function Student(firstName, middleInitial, lastName) {
this.firstName = firstName + "This is my function";
this.middleInitial = middleInitial;
this.lastName = lastName;
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
// 然後你載入一個外部程式,他也有一個Student
// <script type="text/javascript" src="AnotherStudent.js"></script>
function Student(firstName, middleInitial, lastName) {
this.firstName = firstName + "This is another function";
this.middleInitial = middleInitial;
this.lastName = lastName;
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
然後就爆炸了,其中有一個Student會被蓋掉。
所以你若去看一些常用的套件工具,像是jQuery,會發現他們程式都會用IIFE給包起來。
// 像這樣,最後會回傳一個不同的Student給你用
// 但又不會把你自己寫的Student給蓋掉
// 或是你蓋掉他的
// <script type="text/javascript" src="AnotherStudent.js"></script>
var anotherStudent = (function () {
function Student(firstName, middleInitial, lastName) {
this.firstName = firstName + "IIFE";
this.middleInitial = middleInitial;
this.lastName = lastName;
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
return Student;
}());
// IIFE
var user1 = new anotherStudent("Jane", "M.", "User");
// Function
var user2 = new Student("Jane", "M.", "User");
大概就是這樣,我說明的可能不是那麼明確,你可以再找找其他介紹IIFE用法的心得看看。
重點就是避免汙染到全域變數。
Hi,wingkawa,感謝你回答,我剛學程式碼,希望可以教學相長,一起來討論一下吧
我剛做了一個錯誤示範,不曉得是不是你要表達的,如下
// 先聲明一個 Student 函式
function Student(firstName) {
this.firstName = firstName + "尚未改變";
}
// 不使用 IIFE
var anotherStudent = function Student(firstName) {
this.firstName = firstName + "已經改變";
}
// 不使用 IIFE
var user1 = new anotherStudent("Jane");
// Function
var user2 = new Student("Jane");
// 測試 Student 結果有沒有受到改變
console.log(Student); // "... 尚未改變"
我有再去研究一下IIFE,這邊可以發現其實沒有汙染,我在猜,注意!以下都是假設:
對於anotherStudent 來說,它被賦予的函式其實跟匿名函式是一樣的,或者說因為我有var anotherStudent
,在此前提,記憶體存放的位置是不一樣的,所以有沒有IIFE是不會有影響的。
不曉得你可不可以提出個錯誤範例~
https://blog.gtwang.org/programming/defining-javascript-functions/
var anotherStudent = function Student(firstName) {
this.firstName = firstName + "已經改變";
}
上面這個寫法好像叫做 named function expression
欸...其實像這樣寫
var anotherStudent = function Student(firstName) {
this.firstName = firstName + "已經改變";
}
你會發現,如果你要使用function時,其實是用anotherStudent()
而不是Student()
(用Student()
會告訴你not defined)
var user1 = new anotherStudent('Peter'); // { firstName: 'Peter'}
var user2 = new Student('Peter'); // Student is not defined
換言之後面的 = function Student(firstName) {
是多寫的。
所以你的範例其實跟這個一樣
// 先聲明一個 Student 函式
function Student(firstName) {
this.firstName = firstName + "尚未改變";
}
// 不使用 IIFE
function anotherStudent(firstName) {
this.firstName = firstName + "已經改變";
}
// 不使用 IIFE
var user1 = new anotherStudent("Jane");
// Function
var user2 = new Student("Jane");
// 測試 Student 結果有沒有受到改變,因為本來就是使用不同的 function
console.log(Student); // "... 尚未改變"
至於污染的問題,看看這樣有沒有比較好理解。
首先有一個前提,那就是開發程式往往是很多人協作,所以程式能模組化是更好的。
這裡是你的主程式Student.js
function Student(firstName, middleInitial, lastName) {
this.firstName = firstName;
this.middleInitial = middleInitial;
this.lastName = lastName;
this.fullName = firstName + " " + middleInitial + " " + lastName;
}
// 你會用這個函式來 new 一個學生出來
var student = new Student("Jane", "", "M.");
// student.fullName = "Jane M."
使用起來一切都很美好,但有一天你的同事寫了另一支程式StudentExpand.js
,提供一些你原本沒有的功能
/**
一些其他函式,像是輸入學生成績之類的
*/
// 其中有一個函式,和你的函式同名
function Student(firstName, middleInitial, lastName) {
this.firstName = firstName;
this.middleInitial = middleInitial;
this.lastName = lastName;
// fullName 組合方式和原本不同
this.fullName = lastName + " " + middleInitial + " " + firstName;
// 有一些紀錄成績的屬性
this.gradeCode = 0;
this.gradeMath = 0;
}
你會在html
裡載入他的程式來使用
<!-- 這是你的 -->
<script type="text/javascript" src="/Peter/Student.js"></script>
<!-- 這是同事的 -->
<script type="text/javascript" src="/wingkawa/Student.js"></script>
然後一切都不美好了,你new出來的學生變成壞學生跟你唱反調(原本很乖的,一定是交了壞朋友)
var student = new Student("Jane", "", "M.");
// student.fullName = "M. Jane"
而IIFE,是能夠讓兩個人的程式能夠不起衝突的其中一種方式
/wingkawa/Student.js
var WStudent = (function(first, middle, last) {
function Student(first, middle, last) {
this.fullName = last + " " + middle + " " + first;
/**
當然其他屬性和一些有的沒的
*/
}
return Student;
})();
這麼一來,你的學生還是你的學生,他的學生就是他的學生,彼此都是好學生
var student = new Student("Jane", "" ,"M.");
var wStudent = new WStudent("Jane", "", "M.");
看起來好像請你同事換一個名稱就行了,但原本他整個Studetn.js
檔裡面是裸身的,一大堆 function 都赤裸裸地開趴,要人工檢查每個function name有沒有跟你重複,有重複就要改,很不容易吧?
第一點,其實觀念上有些不對。
var anotherStudent = function Student(firstName) {
this.firstName = firstName + "已經改變";
}
anotherStudent在這邊是變數,可以指向各種物件,然後上面的範例指的是將Student這個function放在anotherStudent這個變數之中(指向Student這個function),所以你的文件裡面有宣告的是anotherStudent而Student這個function是被放在變數裡面的,所以你沒辦法直接呼叫他,必須先使用anotherStudent這個變數,把Student這個function取出來,然後用小括號呼叫他執行。
可以試著將 anotherStudent console.log()出來,可以看到他是一個function。
第二點,你後面舉的例子之所以不打架,是因為你把變數名稱區別開來了,不是因為你用IIFE的方式,你new的東西本身就不一樣了,兩個東西是不會污染的,會污染的是裡面的變數,如果跟你全域的變數名稱相同的時候,可能就會互相汙染。
一些見解提出來討論。有錯誤再麻煩指正囉。
第一點你說的對,是我太不嚴謹了
關於第二點,其實我想說的是,引入外部程式時,若外部程式沒有多包一層處理,會很容易發生命名衝突的問題。
不過老實說,我自己也搞得很混亂了XD
這兩天一直在想變數污染的問題,但在function內宣告的變數,其實都只作用在function內,根本不會影響到外面不是嗎?
var aVar = 'i am a variable';
function foo() {
var aVar = 'another variable';
var bVar = 'test';
}
foo();
console.log(aVar); // i am a variable
console.log(bVar); // not defined
若不是function,就有影響了
var aVar = 'i am a variable';
if (true) {
var aVar = 'another variable';
var bVar = 'test';
}
console.log(aVar); // another variable
console.log(bVar); // 然後全域變數會多長一個bVar出來
用 let
就可限定變數在scope內
var aVar = 'i am a variable';
if (true) {
let aVar = 'another variable';
}
console.log(aVar); // i am a variable
var aVar = 'i am a variable';
function foo() {
aVar = 'another variable';
bVar = 'test';
}
foo();
試試看這樣,如果在function裡面你有var的話,的確只在裡面有效,但如果你沒有用var宣告,那他就會往外面一層去找有沒有這個變數,所以是會污染的唷~
對啊,但是IIFE同樣也有這樣的問題
var aVar = 'i am a variable';
(function() {
aVar = 'another variable';
bVar = 'test';
})()
console.log(aVar); // another variable
console.log(bVar); // test
想想也是很正常,javascript會去找上一層,而上一層就是window
所以我才會開始混亂,啊不是說好的IIFE可以處理變數污染的問題嗎?
後來去查一下ES6改變了什麼,才發現似乎ES6已經把這些問題處理掉了
http://es6-features.org/#BlockScopedVariables
所以IIFE很單純就是一個會「立即執行的匿名函式」而已。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
class Greeter2 extends Greeter { };
把這段扣拿去編譯以後,會發現這樣很容易實現繼承的概念。
我不太懂 extends,且那邊第一次看到 Greeter{}
而不是 Greeter()
,給我一些時間研究一下。
抱歉,沒有回答的很完整,extends是物件導向的繼承概念,可以朝這個方向查詢一下。
簡單的講,就是假如說你今天要做一個動物園,裡面有Dog、Cat、Bird的Class。
每個動物都有相同的屬相像是: name、sex、color
但每個動物也都有不同的技能。
這個時候你就可以先寫一個Animal的Class作為父類別,
把共通的屬性寫在Animal這個Class上面,
其他動物只要繼承Animal這個類別,就可以有共通的方法,這個時候再去寫不同動物的技能,可以減少code的重複撰寫。
class Animal {
name: string;
color: string;
sex: string;
constructor(name: string,color: string,sex: string) {
this.name = name;
this.color = color;
this.sex = sex;
}
}
class Dog extends Animal {
bark() {
console.log('汪汪!')
}
}
var dog = new Dog('schnappi', 'whit', 'male');
console.log(dog.name) // schnappi
dog.bark(); // 汪汪!
不知道這樣有沒有解答到你的問題
喔喔~原來是這樣的概念,就是extend(複製、延伸)原本的屬性,然後還可新增方法或是物件屬性。
不知道什麼是物件導向,但是我的理解是這很模組化!
其實我一開始是跑去看他編譯後的JS,我完全矇掉了,所以才想說需要一點時間去研究 ( 跑去研究它編譯後的JS... XD
這解釋真是太棒了,我相信這解釋對有相同需求的人也會有幫助的!
有趣的是,我把它去掉 TS 的部分,然後在 JS 上竟然可以應用,看來 TS 的某些優勢也被原生給取代掉了呢
幫貼編譯後的程式
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Greeter = /** @class */ (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
}());
var Greeter2 = /** @class */ (function (_super) {
__extends(Greeter2, _super);
function Greeter2() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Greeter2;
}(Greeter));
;