Typescript的學習筆記_溫故javascript一些令人詬病的問題_typescript型別跟執行方式還有物件

 


Typescript 是由微軟的C#之父Anders Hejlsberg設計出來的一套語言

javascript 本身以前由Netscape推出而當時是為了蹭Java的熱度才取名叫Javascript的


Javascript本身語言有一個缺點那就是沒有強型別的觀念

Typescript則是補足這塊觀念的增強版程式語言

在此以es5大部分瀏覽器都還能支援的標準來做javascript的比較
Typescript (.ts)本身編譯完最終產物仍是javascript (.js)
=======================================

變數作用域的觀念(var,let)

javascript 變數 區域性跟全域性觀念
通常若你用var宣告在特定一個function中就會視為區域性變數
外層無法存取的到

var num1 = 1;
function test_func(){
    var num2 = 2;
    console.log(num1);
    console.log(num2);
}
console.log(num1);
console.log(num2);
num2 is not defined


當你在function中用var宣告並定義變數的時候
只能夠過functino呼叫存取該區域變數的值此時是有局部範圍限制的!!




再來怪異的一個點到底時常看到人家寫的javascript
宣告變數有時有寫var有時又不寫??? 到底有沒有差別?

這裡在function中若去除var
直接只寫一個變數名稱num2


跟直接指派值給num2


都會發現屬於 not defined
所以看起來感覺有var的宣告跟無var的宣告沒有差異
 


而not defined 跟 undefined是不同的涵義
undefined不是錯誤  not defined才是!!

not defined 代表根本不存在沒被宣告跟定義
undefined 則代表有被宣告但沒有被定義值

javascript在尚未初始化前不會知道該型別是捨麼


var num1;
function test_func(){
    num2=2;
    console.log(num1);
    console.log(num2);
}
console.log(num1);
test_func();
console.log(num2);




這邊會發現原先在functino中定義的num2不管有無var宣告原本都會報錯的
但此時由於上一句呼叫過function再console打印num2就會有資料




所以事實上區域跟全域跟var有無的宣告也是有差異的

區域跟全域在陣列中的撰寫細節
var arr = [];
for(var i=0;i<10;i++){
    arr[i] = function(){
        console.log(i);
    }
}

arr[1]();
arr[2]();
arr[3]();


這裡針對array元素值給function
當中打印對應i的索引

結果怎麼印都是10....
並非預期印出指定的index
因為最外層的for已經跑到最後一圈到10了


到這邊後開始很頭暈.... javascript怎麼變數宣告這麼凌亂
要加var跟不加沒有一致性
很容易寫出BUG


因此之後出現let
var arr = [];
for(let i=0;i<10;i++){
    arr[i] = function(){
        console.log(i);
    }
}
arr[1]();
arr[2]();
arr[3]();




然後開始會看到很多文章跟人分享var跟let就是一個區域一個全域的說法
其實有點不完全正確
但var其實從剛剛一開始實驗也有區域全域的問題!!

常數的觀念

在javascript中 常數const (大部分程式語言都通用的觀念,但通常IDE都會提示錯誤)
則還單純一點也就是一個數不可以改變
初始化多少之後就只能永遠是多少
const PI=3.1415;
console.log(PI);

PI=314;
只是會有一點不太善解人意
不會在寫的過程被提醒不能指派常數的錯誤叮嚀

在到執行階段才會發現問題!



函數的參數多載(同名異式)設計觀念


js中函數 overload 定義
以前傳統作法會採用邏輯運算來做預設值的optional操作設計
function add(a,b){
    a=a || 4 , b = b || 6;
    console.log(a+b);
}
add(4,8);
add(4);
add();



當然在之後推出來新版的ES6則可支援到function預設參數值
function add(a=4,b=6){
    console.log(a+b);
}
add(4,8);
add(4);
add();




物件的觀念


let student = {
    name:"Jack",
    age:25,
    scores:{
        math:76,
        english:88,
        computer:90
    },
    getTotalScore:function(){
        return this.scores.math + this.scores.english + this.scores.computer;
    }
}

console.log(student.age);
console.log(student['name']);
console.log(student.scores.math);
console.log(student.scores['english']);
console.log(student.getTotalScore());




箭頭函數的觀念

一個陣列我們想去打印每個元素的平方結果
傳統作法可以用map

let a = [1,2,3];
let b = a.map(function(i){
    return i*i;
})
console.log(b);




箭頭函數(Lambda)風格
可以省去每次都要寫 function再來一個括號包參數
當function只有一個參數時候可以省去括號直接寫參數搭配箭頭
那主要設計用法除了簡潔寫法還可以解決物件中this使用上指向錯誤的一些存取問題
let a = [1,2,3];
let b = a.map(function(i){
    return i*i;
})
console.log(b);
let c=a.map( i => i*i)
console.log(c);


物件中this使用上的一些問題
架設我們有一個物件obj裡面定義打印something屬性的資料



我們在該方法中又去包一層setTimeout的方法時候

let obj = {
    something : "banana",
    showSomething(){
        //console.log(this.something);
        setTimeout(function(){
            console.log(this.something);
},500); } } obj.showSomething();
會發現存取不到something 變成 undefined



主要是因為在setTimeout 方法中this指向已經不再是當前物件
所以就要改成用 箭頭函式來改寫解決這問題

let obj = {
    something : "banana",
    showSomething(){
        //console.log(this.something);
        //setTimeout(function(){
        //    console.log(this.something);
        //},500);
        setTimeout(()=>{
            console.log(this.something);
        },500);
    }
}
obj.showSomething();




for...in跟for...of迴圈的觀念

在javascript中也很常出錯的就是它的foreach
預設是去抓其index(key)而非值
跟其他一般程式語言不太一樣

let arr = [1,2,3,4,5,6,7,8];

for(var item in arr){
    console.log(item);
}



若是要遍歷值要用arr[key]



那通常for...in 比較少用在陣列 因為陣列的key也就是index
直接for i=0....去做就好沒有捨麼好取的

通常是在針對物件取屬性key時候會比較常用
let obj = {
    thing:"banana",
    price:45
}
for(var item in obj){
    console.log(item);
}



而for...of其實寫法跟for...in差不多
但for...of 是直接印值出來 跟一般其他程式語言foreach觀念叫像

let arr = [1,2,3,4,5,6,7,8];
for(var item of arr){
    console.log("for...of的值:"+item);
}



class的觀念


以前ES5的classes舊式風格
定義一個function同時代表該class的建構子
let Point = function(x,y){
    this.x = x;
    this.y = y;

    this.printAll = function(){
        console.log(this.x + ", " + this.y);
    }
}

到了ES6之後就出現class了

class Point{
    //建構子
    constructor(x,y){
        this.x=x;
        this.y=y;
    }

    printAll(){
        console.log(this.x + ", " + this.y);
    }
}
在javascript中 建構子是直接寫constructor而非class名稱!!!




那在ES6也可以做繼承
用 extends

class Label extends Point{
    constructor(x,y,name){
        super(x,y);//必要 用來初始化指向Base Class
        this.name = name;
    }

    printAll(){
        super.printAll();
        console.log(this.name);
    }
}
let lbl = new Label(1,2,"Label_1");
lbl.printAll();





再來 簡單認識到 TypeScript 
初次要先安裝typescript
npm i typescript -g

typescript非編譯器而是做轉譯(translate)
轉譯(translate): A程式語言翻譯成B程式語言

會負責幫你把寫好的typescript轉成javascript
因為瀏覽器只認得javascript

有無裝成功可透過
tsc --version來做確認




首先附檔名是.ts

預設要宣告變數名稱後頭需補上冒號加上資料型別

而function則一樣需補上冒號加上資料型別
參數也一樣
其餘則跟ES6一樣有預設值直接寫



執行方式則是下
tsc {xxx.ts}
會發現多跑出一個.js的檔案
最終要運行的就是已經被轉譯出來的.js檔案

tsc ex10_typescript.ts & node ex10_typescript.js

var num = 1;
function fun1(a, b) {
    if (a === void 0) { a = 4; }
    if (b === void 0) { b = 8; }
    return a + b;
}
console.log(fun1());


let num:number=1;

function fun1(a:number=4,b:number=8):number{
    return a+b;
}

console.log(fun1());

let tup:[string,number]=["1",5];

let arr:number[] = [1,2,3,4];

若function無返回值則可以用void
常用的型別有
string
number
boolean
any   --> 跟C#的var有點像 (某種特定情況在typescript不能用)

tuple -->元組(跟元祖雪餅沒有關係...)
就跟python有點類似可以混和多種不同資料型別
array

typescript  的 class 屬性
在建構子中會發現套用typescript規範寫
仍然會有錯誤
是因為有缺少修飾詞 private (DI寫法)



class Person{

    constructor(private Name:string){
        this.Name=Name;
    }

    getName(){
        console.log(this.Name);
    }

}



之後就跟一般ES6物件一樣操作
輸出屬性值tsc ex11_Person.ts & node ex11_Person.js

經由typescript翻譯出來javascript



我們可在當前有定義的Person Class在做存取使用
但如果要在外面去引用則要用import 語法 



class定義的所在檔案裏頭的class必須要記得export(輸出)才能給外部他人使用!
export class Person{

    constructor(private Name:string){
        this.Name=Name;
    }

    getName(){
        console.log(this.Name);
    }

}
let person = new Person("Jack");
person.getName();

外部在存取的程式
import {class1名 , class2名....} from 'class定義的所在檔案名稱(不含附檔名)'

import {Person} from './ex11_Person'

let person = new Person("Jack");
person.getName();

















留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header