Javascript.Basic - Bài 4 - tạo đối tượng có và không có prototype.

Giới thiệu

Đối tượng có và không có prototype là vấn đề rất thú vị và thực tế, bởi vì giống như trong bài trước, khi giới thiệu về nước lèo và tô phở, tôi có nói rằng tô phở là một object cuối cùng, người dùng chỉ việc thêm gia vị cho phù hợp rồi dùng nó chứ không có ý nghĩa dùng để tạo ra nhiều tô phở khác. Nên bản thân nó không cần chứa prototype. Nó khác với ý nghĩa nước lèo. Từ nước lèo ta có thể tạo ra nhiều tô phở khác nhau, nên nước lèo có ý nghĩa như một prototype. Dưới đây, tôi muốn giới thiệu về việc làm thế nào để tạo object dạng tô phở và object dạng nước lèo.

Tạo object dạng tô phở

Dưới đây là một số ví dụ về cách thức tạo object dạng tô phở:
var obj = new Object();
var date = new Date();
var str = new String();
Các object trên sẽ không có prototype.
obj.prototype
undefined

date.prototype
undefined

str.prototype
undefined
Xem lại hình minh họa mối quan hệ giữa các built-in object trong bài trước, ta để ý một object được tách ra làm 2 gồm có function object và prototype của object. Ví dụ Date thì bao gồm Date() và Date.prototype. Date() được gọi là một function object.
Một object dạng tô phở thì được tạo ra từ một function object thông qua từ khóa new.
Các object dạng này khi sinh ra chỉ chứa một thuộc tính __proto__, link đến prototype của Object mà nó được new ra.
Object dạng tô phở
Bên dưới sẽ giới thiệu việc tạo object dạng nước lèo cũng là function object

Tạo object dạng nước lèo, hay tạo một function object.

var Person = new Function();
// Person có object prototype
Person.prototype 
> Object {}
// Person sẽ có __proto__ trỏ đến prototype của đối tượng mà nó được "sinh ra"
Person.__proto__ === Function.prototype
>true
// __proto__ của bất kỳ prototype nào cũng mặc định trỏ về prototype của Object.
Person.prototype.__proto__ === Object.prototype
>true
// prototypemột object cụ thể nên các prototype của các object khác nhau sẽ là khác nhau. 
Person.prototype === Object.prototype
>false
Nhắc lại: __proto__ mặc định trỏ đến prototype mà từ đó nó sinh ra. Nói cách khác __proto__ trỏ đến prototype của object mà nó muốn kế thừa.
Hình mình họa:
Object dạng nước lèo

Tổng kết:

Dưới đây là hình tổng kết của bài này.
Create an object in JS

Javascript.Basic - Bài 3 - prototype chain là gì

Giới thiệu

Trong bài trước, tôi đã giới thiệu về các built-in object và mối quan hệ giữa chúng. Trong bài đó tôi có đề cập đến __proto__, và trong bài hôm nay tôi muốn trình bày rõ hơn về nó.

__proto__ là gì

  • __proto__ là 1 thuộc tính của objectA trỏ đến prototype của objectB mà nó muốn kế thừa các thuộc tính của objectB. Đây gọi là prototype chain.
  • Prototype chain có tính chất như sau: nếu một property không được tìm thấy trong nó thì Javascript sẽ cố tìm property này ở prototype object mà __proto__ của nó đang trỏ tới.
  • Hãy xem ví dụ bên dưới:
|objectA|     |objectB.prototype|-------|objectB|
 |           /  |_property1 
 |__proto__ /   |_property2
                |_property3      
objectA.proto === objectB.prototype.
Mặc dù ở objectA nó không có property1, nhưng mà vì __proto__ của objectA trỏ đến prototype của objectB, cho nên nghiễm nhiên là objectA.property1 === objectB.prototype.property1.

Ý nghĩa của prototype chain trong mối quan hệ giữa các object

Quay trở lại mối quan hệ giữa các built-in object:
Javascript Object Relationships
  • Các built-in Object, đều có __proto__ trỏ đến prototype của Function -> sử dụng chung các common (global) function. Tất cả các built-in Object ngoài những function đặc trưng của riêng mình thì đều có thể sử dụng các Function chung như là: apply, bind, call, caller, …
  • Các build-in Object đều có Riêng cho mình 1 Prototype và Prototype này có __proto__ trỏ đến Prototype của Object. Do đó các build-in Object này đều là sự kế thừa và mở rộng từ Prototype của Object.

Ví dụ:

Date __proto__
Bản thân Date() có chứa cho riêng mình các property như là UTC, now, parse, prototype và đồng thời nó cũng có thể sử dụng các property chung được định nghĩa trong Function.prototype.

Javascript.Basic - Bài 2 - Built-in(Global) object và mối quan hệ của nó.

Giới thiệu

Trong bài trước về khái niệm prototype, chúng ta đã thấy prototype-base programing là dạng lập trình dựa trên các object có sẵn. Do đó, nó phải mang trong mình các object có sẵn đó, mà thuật ngữ tiếng anh sẽ là built-in object. Trong bài này tôi muốn giới thiệu cho các bạn hình ảnh mối quan hệ đó.

Các Built-in Object

  • Object
  • Function
  • Array
  • Number
  • Boolean
  • String
  • Date
  • CustomUDF
Ở trên chỉ là vài object có sẵn. Nếu các bạn cảm thấy khái niệm object có sẵn là khó hiểu thì hãy hình dung như nó đóng vai trò như là các Data Type trong Java hay C. Rõ ràng bạn có thể có ngay một đối tượng date từ Date mà không cần phải tốn thời gian định nghĩa Date là gì.
Bằng việc cung cấp sẵn các built-in object này, Javascript giúp người có thể nhanh chóng “chế tạo” các object cho riêng mình. Công việc sáng tạo này giống như là đang đứng trên vai người khổng lồ. Và cũng bởi vì các programer có thể nhanh chóng “chế tạo” các object cho riêng mình từ những thứ có sẵn cho nên việc tìm hiểu sâu về prototype cũng sẽ lười đi. Đó cũng là lý do tôi muốn chia sẽ với các bạn về loạt bài prototype này.

Mối quan hệ

Javascript Object Relationships
Các lưu ý về hình minh họa trên:
- Các Built-in Object đều có Prototype, thể hiện qua đường màu đỏ ngắt quãng.
- Các đường mũi tên màu đen là các link __proto__
- Ở hình trên, tác giả chủ ý vẽ dấu đóng mở ngoặc ở sau tên các object,như là Object(), Function(), Array(), Number()…Ở đây chúng ta hãy tạm gọi là các function object.
- Trong Prototype cũng có các link __proto__
- Tất cả các __proto__ của các Built-in Object đều trỏ tới Funtion.Prototype. Do đó bạn hãy thử bật chrome developer console và gõ typeof Object xem thử nhé.
- Trước mắt các bạn chỉ cần khắc dấu hình ảnh này, chưa cần tìm hiểu sâu về mối quan hệ giữa chúng. Việc tìm hiểu sâu đòi hỏi phải có hiểu biết về __proto__ trong bài sau.
- Và hãy nhớ mối quan hệ sau: object() — Prototype => function object mới có prototype. Và bởi vì object() là một function, prototype là một object, cho nên proto của nó sẽ trỏ về Prototype của Function hoặc là Object.

Javascript.Basic - Bài 1 - Khái niệm prototype

Giới thiệu

Javascript là ngôn ngữ quen thuộc với các bạn làm Web, hầu như ai làm Web cũng có thể bắt tay vào viết mã javascript đơn giản được ngay mà không tốn quá nhiều thời gian. Ở đây tôi muốn nhấn mạnh từ đơn giản là bởi vì khi mà tự mình phải viết một thư viện nho nhỏ thì sẽ gặp ngay các khái niệm thuộc hạng cơ bản của Javascript như là prototype. Nếu bạn không nắm vững prototype trong Javascript là gì thì việc rơi vào tình trạng mơ hồ khi lập trình Javascript ở mức độ nâng cao sẽ là đương nhiên. Đó là lý do tôi muốn viết bài này để chia sẽ những hiểu biết của tôi về prototype trong Javascript.

Class

  • Định nghĩa các đối tượng mà nó đại diện. Class không phải là object.
  • Class-base programing: tôi xin trích một đoạn ở trong trang: https://en.wikipedia.org/wiki/Class-based_programming, “inheritance is achieved by defining classes of objects.”. Tôi nhấn mạnh là bạn cần định nghĩa class để tạo object.

Prototype

  • Prototype là một object mà trong đó chứa các đặc điểm ban đầu. Tôi gọi prototype là phôi mẫu.
  • Prototype-base programing: tôi xin trích một đoạn ở trong trang: https://en.wikipedia.org/wiki/Prototype-based_programming, “inheritance is performed via process of cloning existing objects that serve as prototype”. Tôi nhấn mạnh là bạn clone các object có sẵn để tạo ra các object mới.

Class vs Prototype

Từ các khái niệm trên, tôi mới phát họa ra hình ảnh sau để minh họa rõ hơn sự khác nhau giữa 2 dạng lập trình.

class vs prototype

Như hình minh họa ở trên, nếu ở class-base programing thì công thức nước lèo đóng vai trò là class, nó chứa đựng thông tin mà bạn cần mô tả (định nghĩa) trước, từ công thức đó bạn sẽ tạo ra được nồi nước lèo, nồi nước lèo ở đây là object. Còn ở prototype-base programing, thì bạn hãy hình dung là nó đã có sẵn nồi nước lèo cơ bản này rồi, và bạn chỉ việc clone nó ra thêm nguyên liệu để tạo ra một dạng nước lèo khác. Việc tạo ra dạng nước lèo khác này không cần thông qua việc phải mô tả (định nghĩa) trước công thức của nó mà đơn thuần là sử dụng (clone) nồi nước lèo cơ bản rồi sau đó thêm thắt theo ý mình mà ra. Nồi nước lèo được tạo từ nồi nước lèo cơn bản, nếu nó vẫn đóng vai trò là nồi nước lèo thì nó vẫn được xem là prototype, còn tô phở, một khi được tạo ra, nó không còn ý nghĩa dùng để tạo ra đối tượng nào khác nữa thì nó là một instance và nó không chứa prototype nữa. Do đó:
var obj = new Object();
thì obj không có prototype (nó cũng giống như tô phở ở trên). Các bạn hãy lưu ý điểm này. Còn làm sao có thể tạo object mà có prototype thì tôi sẽ trình bày trong các bài tiếp theo.

Giới thiệu webpack

Khi tôi tìm hiểu về React.js, tôi thấy họ có dùng webpack để “bundle” các module. Trong các bài giới thiệu về Reactjs trước tôi có dùng Browserify để “bundle” các module của Node.js thấy đơn giản và cũng khá hay. Tôi có qua trang tài liệu của webpack-doc để thử tìm hiểu thì thấy hơi khó nhai nên tôi quyết định viết blog để có thể tìm hiểu sâu hơn cũng như có thể chia sẽ với những ai muốn tìm hiểu về nó.

Webpack là gì

Ngay tại trang document:
http://webpack.github.io/docs/what-is-webpack.html

webpack is a module bundler.

webpack takes modules with dependencies and generates static assets representing those modules.

Tôi nghĩ cách hiểu nhanh nhất về nó là ta phải “đụng tay đụng chân” với nó thôi.

Hướng tìm hiểu

  1. Bundle các file JS thành 1 file JS.
  2. Bundle ra các file JS chính từ nhiều file JS
  3. Chia file JS và load khi cần
  4. Nhúng HTML vào trong file JS
  5. Nhúng CSS vào trong file JS
  6. Nhúng file hình Base64 vào trong file JS

1. Bundle các file JS thành 1 file JS

Chuẩn bị:

./
 |_app.js
 |_sub.js
 |_dist
    |_index.html

app.js

var sub = require('./sub.js');
sub("Hello world");

sub.js

module.exports = function(msg){
  alert(msg);
}

index.html

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack Tutorial Demo</title>
  </head>
  <body>
    <script src="bundle.js"></script>
  </body>
</html>

Nếu bạn lười thực hiện các bước trên, các bạn có thể clone từ github repo demo của tôi:

https://github.com/phungnc/webpack-tut/tree/master/demo01

Bundle command:

webpack app.js dist/bundle.js

Sau đó chạy file index.html thì nó sẽ hiển thị message “Hello world”.

Bundle command trên có cú pháp như khi sử dụng Browserify (browserify app.js > dist/bundle.js). Tuy nhiên khác với Browserify, Webpack có thể config việc bundle đó trong file: webpack.config.js. Hãy tạo file webpack.config.js đồng cấp với file app.js

module.exports = {
  entry: {
    app: './app.js'
  },
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  }
}

Sau đó chỉ cần bạn chạy lệnh

webpack

Thì nó cũng tạo ra file bundle.js giống như trước.

2. Bundle ra các file JS chính từ nhiều file JS

Source code demo:
https://github.com/phungnc/webpack-tut/tree/master/demo02

webpack.config.js

module.exports = {
  entry: {
    app: './app.js',
    list: './list.js',
    item: './item.js',
  },
  output: {
    path: __dirname + '/dist',
    filename: '[name].js'
  }
}

Run webpack command, hãy xem output in ra:

Hash: 42caea66a4d1045d2c6c
Version: webpack 1.12.2
Time: 68ms
  Asset     Size  Chunks             Chunk Names
 app.js  1.56 kB       0  [emitted]  app
item.js  1.56 kB       1  [emitted]  item
list.js  1.56 kB       2  [emitted]  list
   [0] ./app.js 43 bytes {0} [built]
   [0] ./item.js 43 bytes {1} [built]
   [0] ./list.js 44 bytes {2} [built]
   [1] ./sub.js 47 bytes {0} {1} {2} [built]

Nó built ra các file trong thư mục dist các file app.js,list.js, item.js tương ứng đã được config trong file webpack.config.js. Khả năng có thể build ra nhiều file js cũng là điểm thú vị của webpack.

Nhưng mà, nếu ta xem trong các file app.js, list.js hay item.js thì có phần source code cả 3 file đều trùng nhau:

/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};

/******/    // The require function
/******/    function __webpack_require__(moduleId) {

/******/        // Check if module is in cache
/******/        if(installedModules[moduleId])
/******/            return installedModules[moduleId].exports;

/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = true;

/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }


/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;

/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/

Để giải quyết vấn đề này ta có thể sử dụng plugin:

webpack.config.js

var webpack = require('webpack');
module.exports = {
  entry: {
    app: './app.js',
    list: './list.js',
    item: './item.js',
  },
  output: {
    path: __dirname + '/dist',
    filename: '[name].js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin('app','app.js')
  ]
}

Chạy lại command

webpack

Bây giờ bạn thử check lại trong các file dist/list.jsdist/item.js xem. Phần source code giống nhau bây giờ chỉ còn lại ở file dist/app.js.

3. Chia file JS, chỉ load khi cần thiết.

Nếu bạn nào đã từng dùng qua requirejs rồi thì sẽ dễ dàng hiểu phần này hơn.
Ta có thể gọi chức năng này là module loader, tức là ta có thể load một module khi nào cần thiết. Khác với việc tạo một file bundle bao gồm tất tần tật các module JS vào, với webpack ta có thể cấu hình để chỉ load module cần thiết khi cần thiết thôi. Việc này có lợi cực kỳ khi hệ thống ngày càng lớn, khi đó nếu chỉ dùng 1 file bundle thì việc load ban đầu sẽ trở nên chậm chạp làm giảm performance của app, ngược lại việc load module một cách bất đồng bộ sẽ giúp tăng performance của app.

Ta sẽ demo bằng cách là trong file app.js sẽ load module sub.js sau 3 giây.

Source code:
https://github.com/phungnc/webpack-tut/tree/master/demo04

app.js

window.setTimeout(function() {
    require.ensure([],function(sub) {
        var sub = require('./sub');
        sub('App');
    });
},3000);

Run WebPack command

webpack

Hãy thử mở file index.html, sau khi mở lên, 3 giây sau thì nó sẽ alert “App”. Bạn có thể dùng Chrome Develop Tool để xem tiến trình load này.

4. Nhúng file HTML vào trong file JS

Bằng việc sử dụng module html-loader, ta có thể load file HTML vào trong file JS.
Trước hết ta phải install html-loader

Source code:
https://github.com/phungnc/webpack-tut/tree/master/demo04

npm install html-loader --save-dev

Sau đó trong webpack.config.js file:

module.exports = {
  entry: {
    app: './app.js'
  },
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
      loaders: [
          { test: /\.html$/, loader: 'html-loader' },
      ]
  }
}

Thêm phần load module html-loader.

Về phần source code, chuẩn bị file HTML có nội dung nào đó như là:

container.html

<div id="container">
    <section>
        <h2>Web pack is Cool</h2>
          <p>webpack is a module bundler.</p>
          <p>webpack takes modules with dependencies and generates static assets representing those modules.</p>
    </section>
</div>

app.js

var container = require('./container.html');
document.body.innerHTML = container;

Và run WebPack command:

webpack

Với việc nhúng HTML từ bên ngoài vào trong JS, thì chúng ta sẽ dễ nghĩ đến hướng chia cấu trúc trang web thành các template, component. Phần này có lẽ tôi sẽ giới thiệu trong các bài viết sau.

5. Nhúng file CSS vào trong file JS

Tương tự như việc load file HTML vào trong file JS, ta cũng có thể sử dụng các module style-loadercss-loader để nhúng CSS vào trong file JS.

Source code:
https://github.com/phungnc/webpack-tut/tree/master/demo05

npm install style-loader css-loader --save-dev

css-loader: tạo chuỗi css và nhúng vào trong file JS.

style-loader: ghi style vào trong tag

webpack.config.js

module.exports = {
  entry: {
    app: './app.js'
  },
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
      loaders: [
          { test: /\.html$/, loader: 'html-loader' },
          { test: /\.css$/, loaders: ['style-loader', 'css-loader']},
      ]
  }
}

Sau đó bạn có thể chuẩn bị tùy ý style mà bạn thích trong file style.css như là:

style.css

#container {
  width: 800px;
  margin: 0 auto;
}
section {
  text-align: center;
}

Gọi css vào trong file app.js

require('./style.css');
var container = require('./container.html');
document.body.innerHTML = container;

Run webpack

webpack

6. Nhúng file image dạng base64 vào trong file JS

Sau cùng, chúng ta thử nhúng file hình vào trong file JS. Ta sử dụng url-loader

npm install url-loader --save-dev

Cũng tương tự như những phần trên, cho nên tôi sẽ không giới thiệu lại, các bạn có thể tham khảo source code bên dưới:

Source code
https://github.com/phungnc/webpack-tut/tree/master/demo06