بازی Snake با 30 خط کد JavaScript
Anders Hejlsberg و معرفی TypeScript
آموزش TypeScript #1
سوالی که همیشه من داشته ام این است .... چرا خود زبان JavaScript را تغییر نمیدهند؟
مسلما TypeScript و CoffeeScript برای برطرف کردن ضعف JavaScript بوجود آمده اند اما چرا خود مشکل را برطرف نمیکنند؟
میتوانستند همانند ارائه HTML5 و CSS3 نسخه جدیدی از JavaScript ارائه کنند که سختیهای کار با JavaScript را برطرف کرده باشند!
عبارت زیر را در گوگل جستجو کنید
JavaScript complete reference rapidshare
هدف: استفاده از کتابخانهی jsSHA
میخواهیم در یک برنامهی AngularJS 2.0، از کتابخانهی jsSHA استفاده کرده و هش SHA512 یک رشته را محاسبه کنیم.
تامین پیشنیازهای اولیه
میتوان فایلهای این کتابخانه را مستقیما از GitHub دریافت و به پروژه اضافه کرد. اما بهتر است اینکار را توسط npm مدیریت کنیم. به همین جهت فایل package.json آنرا گشوده و سپس مدخل متناظری را به آن اضافه کنید:
"dependencies": { // ... "jssha": "^2.1.0", // ... },
بارگذاری فایلهای کتابخانه به صورت پویا
یک روش استفاده از این کتابخانه یا هر کتابخانهی جاوا اسکریپتی، افزودن مدخل تعریف آن به صفحهی index.html است:
<script src="node_modules/jssha/src/sha512.js"></script>
// map tells the System loader where to look for things var map = { // ... 'jssha': 'node_modules/jssha/src' }; // packages tells the System loader how to load when no filename and/or no extension var packages = { // ... 'jssha': { main: 'sha512.js', defaultExtension: 'js' } };
به این ترتیب هر زمانیکه کار import این کتابخانه صورت گیرد، بارگذاری پویای آن انجام خواهد شد. به علاوه ابزارهای بسته بندی و deploy پروژه هم این فایل را پردازش کرده و به صورت خودکار، کار bundling، فشرده سازی و یکی سازی اسکریپتها را انجام میدهند.
استفاده از jsSHA به صورت untyped
پس از دریافت بستههای این کتابخانه و مشخص سازی نحوهی بارگذاری پویای آن، اکنون نوبت به استفادهی از آن است. در اینجا منظور از untyped این است که فرض کنیم برای این کتابخانه، فایلهای typings مخصوص TypeScript وجود ندارند و پس از جستجوی در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped نتوانستهایم معادلی را برای آن پیدا کنیم. بنابراین فایل جدید untyped-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
import { Component, OnInit } from '@angular/core'; var jsSHA = require("jssha"); // ==> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions //declare var jsSHA: any; // ==> this requires adding <script src="node_modules/jssha/src/sha512.js"></script> to the first page manually. @Component({ templateUrl: 'app/using-third-party-libraries/untyped-sha.component.html' }) export class UnTypedShaComponent implements OnInit { hash: String; ngOnInit(): void { let shaObj = new jsSHA("SHA-512", "TEXT"); shaObj.update("This is a test"); this.hash = shaObj.getHash("HEX"); } }
<h1>SHA-512 Hash / UnTyped</h1> <p>String: This is a test</p> <p>HEX: {{hash}}</p>
هر زمانیکه فایلهای typing یک کتابخانهی جاوا اسکریپتی در دسترس نبودند، فقط کافی است از روش declare var jsSHA: any استفاده کنید. در اینجا any به همان حالت استاندارد و بینوع جاوا اسکریپت اشاره میکند. در این حالت برنامه بدون مشکل کامپایل خواهد شد؛ اما از تمام مزایای TypeScript مانند بررسی نوعها و همچنین intellisense محروم میشویم.
در این مثال در hook ویژهای به نام OnInit، کار ساخت شیء SHA را انجام داده و سپس هش عبارت This is a test محاسبه شده و به خاصیت عمومی hash انتساب داده میشود. سپس این خاصیت عمومی، در قالب این کامپوننت از طریق روش interpolation نمایش داده شدهاست.
دو نکتهی مهم
الف) اگر از روش declare var jsSHA: any استفاده کردید، کار بارگذاری فایل sha512.js به صورت خودکار رخ نخواهد داد؛ چون ماژولی را import نمیکند. بنابراین تعاریف systemjs.config.js ندید گرفته خواهد شد. در این حالت باید از همان روش متداول افزودن تگ script این کتابخانه به فایل index.html استفاده کرد.
ب) برای بارگذاری پویای کتابخانهی jsSHA بر اساس تعاریف فایل systemjs.config.js از متد require کمک بگیرید:
var jsSHA = require("jssha");
استفاده از jsSHA به صورت typed
کتابخانهی jsSHA در مخزن کد https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/jssha دارای فایل d.ts. مخصوص خود است. برای نصب آن از یکی از دو روش ذیل استفاده کنید:
الف) نصب دستی فایلهای typings
npm install -g typings typings install jssha --save --ambient
ب) تکمیل فایل typings.ts
{ "ambientDependencies": { // ... "jssha": "registry:dt/jssha#2.1.0+20160317120654" } }
پس از نصب فایلهای typings این پروژه، به فایل main.ts مراجعه کرده و مدخل ذیل را به ابتدای آن اضافه کنید:
/// <reference path="../typings/browser/ambient/jssha/index.d.ts" />
در ادامه فایل جدید typed-sha.component.ts را با محتوای ذیل به پروژه اضافه کنید:
import { Component, OnInit } from '@angular/core'; //import { jsSHA } from "jssha"; import * as jsSHA from "jssha"; // ===> var jsSHA = require("jssha"); // ===> loads `sha512.js` file dynamically using `systemjs.config.js` file definitions @Component({ templateUrl: 'app/using-third-party-libraries/typed-sha.component.html' }) export class TypedShaComponent implements OnInit{ hash: String; ngOnInit(): void { let shaObj = new jsSHA("SHA-512", "TEXT"); shaObj.update("This is a test"); this.hash = shaObj.getHash("HEX"); } }
در اینجا تنها نکتهی مهم و جدید نسبت به روش قبل (استفاده از jsSHA به صورت untyped)، روش import این کتابخانه است. روش "import * as jsSHA from "jssha به عبارت var jsSHA = require("jssha") ترجمه میشود که در نهایت سبب بارگذاری خودکار فایلهای jssha بر اساس تعاریف مداخل آن در فایل systemjs.config.js میگردد.
کدهای کامل این پروژه را از اینجا میتوانید دریافت کنید.
رشتهها در ES 6
در بیشتر زبانهای برنامهنویسی قابلیتی تحت عنوان String Interpolation وجود دارد. منظور، فرآیند جایگزین کردن مقادیر، با یکسری placeholder درون یک رشته است. در نسخههای قبلی جاوا اسکریپت محدودیتهایی در استفاده از رشتهها وجود داشت و امکان انجام این کار به صورت توکار مهیا نبود. یعنی برای پیادهسازی این قابلیت میتوانستیم با تغییر prototype شیء String و یا روشهای دیگری اینحالت را پیادهسازی کنیم (+):
// First, checks if it isn't implemented yet. if (!String.prototype.format) { String.prototype.format = function() { var args = arguments; return this.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] != 'undefined' ? args[number] : match ; }); }; } "Hello, {0}, I'm a simple {1}, Today is: {2}".format("World", "String", new Date()); // Output "Hello, World, I'm a simple String, Today is: Tue Dec 29 2015 10:21:10 GMT+0330 (Iran Standard Time)"
اما در ES 6 با کمک قابلیتی تحت عنوان template string این محدودیتها به طور قابل ملاحظهایی کاهش پیدا کرده است. در واقع یک template string، یک رشتهی جاوا اسکریپتی است که به جای (" ") و یا (' ') درون دو کاراکتر (` `) یا به اصطلاح back-tick character محصور خواهد شد. این ویژگی در سناریوهای مختلفی کاریرد دارد. از این ویژگی میتوانیم جهت الحاق رشتهها استفاده کنیم. به عنوان مثال میتوانیم کد زیر را:
let category = "music"; let id = 2112; let url = "http://apiserver/" + category + "/" + id;
با کمک template string به اینصورت بازنویسی کنیم:
let category = "music"; let id = 2112; let url = `http://apiserver/${category}/${id}`;
و یا میتوانیم مثال ابتدای مطلب را به اینصورت بازنویسی کنیم:
console.log(`Hello, ${"World"}, I'm a simple ${"String"}, Today is: ${new Date()}`);
همانطور که عنوان شد برای استفاده از این قابلیت باید رشتهی موردنظر را درون دو کاراکتر (` `) قرار دهیم. سپس درون این کاراکترها میتوانیم literal text و همچنین یکسری placeholder جهت جایگزین کردن با مقادیر و عبارات موردنظر داشته باشیم. این placeholderها نیز با استفاده از سینتکس { }$ قابل تعریف هستند. لازم به ذکر است که عبارت موردنظرمان را باید درون دو علامت { } بنویسیم. مقادیر درون این دو علامت میتوانند هر عبارت معتبر جاوا اسکریپتی باشند:
let a = 5; let b = 10; console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`); // "Fifteen is 15 and // not 20."
در کد فوق متغیرهای a و b درون placeholderهای مربوطه جایگزین خواهند شد. همانطور که مشاهده میکنید، این سینتکس نسبت به سینتکس + که برای الحاق رشتهها قبلاً مورد استفاده قرار میگرفت خیلی بهتر و خواناتر است.
به صورت خلاصه:
- کد درون placeholder میتواند هر عبارت جاوا اسکریپتی باشد.
- اگر مقدار درون placeholder یک رشته نباشد٬ توسط متد toString به رشته تبدیل خواهد شد.
- اگر بخواهید درون template string از یک کاراکتر backtick استفاده کنید٬ میتوانید به این صورت عمل کنید:
`\`` // یا "`"
Multiline Strings
console.log(`string text line 1 string text line 2`); // "string text line 1 // string text line 2"
همانطور که مشاهده میکنید، template string از متنهای چندخطی نیز به خوبی پشتیبانی میکند. به عنوان مثال اگر رشتهی فوق را درون گیومه مینوشتیم میبایستی از سینتکس + برای الحاق دو خط فوق استفاده میکردیم:
console.log("string text line 1\n"+ "string text line 2"); // "string text line 1 // string text line 2"
محدودیتهای template strings
- به صورت خودکار کارکترهای خاص را برای شما escape نمیکند (جهت جلوگیری از آسیبپذیریهای XSS).
- به صورت کامل از کتابخانههایی جهت اعمال internationalization پشتیبانی نمیکند.
- جایگزینی برای کتابخانههایی مانند Mustache و Nunjucks نیست.
var x = 1; var y = 3; var result = upper `${x} + ${y} is ${x+y}`; console.log(result); // Output // 1 + 3 IS 4
let upper = function(strings, ...values){ let result = ""; for(var i = 0; i < strings.length; i++){ result += strings[i]; if(i < values.length){ result += values[i]; } } return result.toUpperCase(); };
strings = ["", " + ", " is ", ""]; values = [1, 3, 4];
یک مثال عملی
میخواهیم یک tag template ایجاد کنیم که به انتهای اعداد درون یک تملپت، مقدار "تومان" را اضافه کرده و خود عدد را نیز به صورت سه رقم سه رقم جدا کند. میخواهیم رشتهی زیر همراه با مقادیر آن:
var name = "سیروان عفیفی"; var price = 150000; var text = withToman `${name} با تشکر از خرید شما, مبلغ قابل پرداخت: ${price}`; alert(text);
function withToman(strings, ...values) { return strings.reduce( function (s, v, idx) { if(idx > 0) { if(typeof values[idx - 1] == "number") { s += `${values[idx - 1].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} تومان` } else { s += values[idx -1]; } } return s + v; }, ""); }