Object can contain:
Create an object:
// reserve in memory
var person = new Object();
// set properties
person["name"] = "Tony";
person["country"] = "Vietnam";
// access object properties
console.log(person)
console.log(person.name)
console.log(person.country)
var nameProperty = "name";
console.log(person["nameProperty"])
// set other object inside object
person.address = new Object();
person.address.street = "Yen Do";
person.address.city = "Ho Chi Minh";
// access object inside object
console.log(person.address.street);
console.log(person["address"]["street"];
Thường chỉ nên dùng DOT để truy cập giá trị của Object
Chỉ nên dùng STRING ARRAY khi cần truy cập giá trị bằng tên động
var nameProperty = "name";
console.log(person["nameProperty"])
Object Literals
// new Object(), reserve in memory
var person = {};
var Tony = {
name: 'Tony',
country: 'Vietnam',
address: {
street: 'Yen Do',
city: 'Ho Chi Minh'
}
};
// function to call an object
function greet(person) {
console.log('hi ' + person.name)
}
greet(Tony); // 'hi Tony'
// create object on the fly
greet( {name:'Mary',country:'US'} ); // 'hi Mary'
JSON & Object Literals
// object
var objectName = {
name: 'Tony',
country: 'Vietnam',
address: {
street: 'Yen Do',
city: 'Ho Chi Minh'
}
};
// convert to JSON string
console.log( JSON.stringlify(objectName) )
// JSON
{
"name": "Tony",
"country": "Vietnam",
"address": {
"street": "Yen Do",
"city": "Ho Chi Minh"
}
}
// convert string to JSON
var jsonVar = JSON.parse( '{ "street": "Yen Do", "city": "Ho Chi Minh"}' )
Function & Object Literals
Functions are Objects
First-class Functions: everything you can do with other types, you can do with funtion.
Assing them to var, pass around, create on the fly Function cũng có:
Function có thêm:
invocable (có thể gọi được) Function Expression: một đoạn mã trả lại một giá trị
Function Statement: một đoạn mã chỉ thực hiện một hành động (so sánh IF hay in ra log)
By Value & By Reference
MUTATE vs NEW OBJECT
‘this’
Seem like a bug in JS, but this is an unusual scenario:
Arrays
arguments and SPREAD
là những gì bạn truyền vào bên trong một function
SPREAD
Function Overloading
Một cách hay để chia nhỏ function, không truyền quá nhiều arguments
Lưu ý Syntax Parser
Nên tự hết dòng bằng dấu chấm phẩy, vì nếu để JS tự thêm chấm phẩy thì sẽ có nhiều bug khó track
Whitespace: xuống hàng, spacing, tabbing
Immediate Invoking Function
Phong cách viết này rất dễ gặp trong các framework nổi tiếng say này.
ĐỂ TÁCH BIỆT NHỮNG FILE JS CÓ BIẾN RIÊNG, CHẠY ĐỘC LẬP, TRẢ VỀ KẾT QUẢ XONG RỒI BIẾN MẤT KHÔNG ẢNH HƯỞNG ĐẾN NHỮNG FILE JS TRÊN HAY DƯỚI, không ảnh hưởng đến Global Object có thể tùy chọn access những biến global nếu thích, nhưng sẽ an toàn Tình huống kinh điển:
Closures
Ngay cả khi 1 hàm đã chạy xong, thì những biến và hàm bên trong hàm đó vẫn còn hiệu lực
Ngay cả khi greet() đã chạy xong thì các biến và hàm bên trong greet vẫn còn hiệu lực.
Tình huống kinh điển: chắc để tạo ra 1 mớ hàm
Nếu muốn lưu biến i chạy vòng lặp và thu gọn các dòng code:
Tạo ra factory function
Closure & Callback
callback là ứng dụng của closure call(), apply(), bind()
Function Borrowing
Mượn hàm của object này, chạy cho object khác
Function Currying
Copy hàm với một bộ giá trị param chỉ định
Tổng kết:
bind có thể thực hiện với object để thay đổi bối cảnh ‘this’ bind có thể thực hiện với ‘this’ để thay đổi param mặc định (’this’ không đổi) bind không thực thi, call và apply thực thi Functional Programming
Code đẹp để tạo ra 1 hàm xử lý mảng
// ví dụ, có 1 mảng, map thành 1 mảng mới với một điều kiện nhất định
var arr1 = [1,2,3];
// ví dụ muốn xử lý mảng rồi tạo ra 1 mảng mới
var arr2 = [];
// đây là cách thường làm
for(var i=0; i < arr1.length; i++) {
// map giá trị của mảng 2 tương ứng với mỗi giá trị của mảng 1...
arr2.push(
// sẽ bằng giá trị mảng 1 nhân với 2
arr1[i] * 2;
)
}
console.log(arr1);
console.log(arr2);
// cách này có hạn chế là phải hard-code hàm
// không thể linh động thay đổi hàm, giá trị cần mutate mảng
// CÁCH XỊN LEVEL 1, viết một hàm first-class, sử dụng những gì đã học
function mapForEach(arr, fn) {
var newArr = [];
// đối với mỗi giá trị của mảng truyền vào
for (var i=0; i < arr.length; i++) {
// tạo một mảng mới...
newArr.push(
// với các giá trị của hàm được truyền vào
fn(arr[i])
)
};
// trả lại mảng mới
return newArr;
}
var arr1 = [1,2,3];
// trả lại giá trị như hàm cũ
var arr2 = mapForEach(arr1, function(item) {
return item * 2;
});
// có thể truyền mọi thứ, từ công thức đến điều kiện boolean [false, false, true]
var arr3 = mapForEach(arr1, function(item) {
return item > 2;
});
// LEVEL MAX, kết hợp với bind() để đơn giản hóa việc gọi hàm hơn nữa
// giả dụ, nếu cần tạo ra 1 hàm so sánh mảng với giá trị được truyền vào (thay vì 2)
var checkPastLimit = function(limiter, item) {
return item > limiter;
}
// dùng bind() để cố định param 'limiter' là 2 cho mỗi giá trị của arr1 ('this')
// sau bind(this, 2) thì chính là tương đương (limiter = 2, item)
var arr4 = mapForEach(arr1, checkPastLimit.bind(this, 2));
// hàm ở trên vẫn còn dư 'this', bây giờ làm cách nào bỏ 'this' luôn
var checkPastLimitSimplified = function(limiter) {
// 3. return object trả lại 1 hàm, với 'this'item' và limiter được truyền vào
return function(limiter, item) {
return item > limiter;
}.bind(this, limiter);
// 1. nhận limiter truyền vào
// 2. bind vào return object với 'this' và limiter
};
var arr5 = mapForEach(arr1, checkPastLimitSimplified(2));
Đọc thêm để hiểu về cách khái niệm cao cấp của functional programming
Có nhiều ví dụ tương tự như ví dụ bên trên
Một mớ hàm xử lý mảng được viết sẵn, nên đọc để học rồi xài
Inheritance (không hay)
Một object thừa hưởng các thuộc tính và hàm thì object khác
propotypal inheritance: easy, flexible, extensible // old, slow-down way to access, DON'T DO
var person = {
name: 'default',
getFullName: function () {
return 'hello' + this.name;
}
}
var tony = {
name: 'Tony'
}
// tony to inherit person method, property
tony.__proto__ = person;
// will look for 'name' in primary object first
console.log( tony.getFullName() )
object a{} has default a.__proto__.method (toString,...)
function b() has default b.__proto__.method (apply, call, bind...)
array c[] has default c.__proto__.method (push, pop...)
// extend is feasible through refelction
//
for (var prop in tony) {
// kiểm tra các prop của object
if(tony.hasOwnProperty(prop)) {
console.log(prop + ': ' + tony[prop])
}
}
var hanah = {}
var bambi = {}
// hợp nhất 3 object bằng hàm underscore.js library mà không cần replace __proto__
_.extend(tony, hanah, bambi);
Builidng Objects
Cách xây dựng 1 object bằng Function Constructor
// function constructor
// có cách mới để tạo object, cách cũ này sẽ biến mất, nhưng vẫn nên hiểu
function Person(firstname, lastname) {
console.log(this);
// 'this' points to a new empty object
this.firstname = firstname;
this.lastname = lastname;
// this will cost memory space, than creating a prototytpal method
this.getFullName = function () {}
console.log('This function is invoked.');
}
// prototypal method added to fucntion BEFORE creating object
// this will save memory space, than putting method directly into objects
Person.prototype.getFullName = function() {
return this.firstname + ' ' + this.lastname;
}
// new = empty object
// return anything in the function 'Person' into an object
var john = new Person('John', 'Doe');
console.log(john);
// nếu không có 'new' sẽ bị undefined
var jane = new Person('Jane', 'Doe');
console.log(jane);
// prototypal method added to fucntion AFTER creating object, still work
Person.prototype.getFormalFullName = function() {
return this.lastname + ', ' + this.firstname;
}
console.log(john.getFormalFullName());
Một số object mặc định của JS
// tạo ra 1 object với giá trị "hello"
var a = new String("hello")
// tìm letter 'o' trong chuỗi
// String.prototype.indexOf(3)
a.indexOf("o")
// tạo ra 1 object với giá trị 3
var a = new Number(3)
// chuyển thành số
// Number.prototype.toFixed()
a.toFixed()
var a = new Array();
Nếu muốn một library xử lý Date, cân nhắc dưới đây:
CẨN THẬN VỚI MẤY CÁI HÀM BUILT-IN NÀY, HIỂU THÔI CHỨ ĐỪNG CÓ XÀI.
Object.create
tạo ra 1 object mẫu, với default properties và methods tạo ra nhiều object khác từ object mẫu properties thay đổi, sẽ ưu tiên các giá trị mới hơn var person = {
firstname: 'Default',
lastname: 'Default',
greet: function() {
return 'Hi ' + this.firstname;
}
}
var john = Object.create(person);
john.firstname = 'John';
john.lastname = 'Doe';
console.log(john);
Nếu sử dụng browser cũ, thì Object.create sẽ chưa hỗ trợ
// polyfill
if (!Object.create) {
Object.create = function (o) {
// chỉ chấp nhận 1 args
if (arguments.length > 1) {
throw new Error('Object.create implementation only accepts the first parameter.');
}
function F() {}
F.prototype = o;
return new F();
};
}
Thêm 1 cách để tạo Object bằng Classes trong ES6
Nếu sử dụng ES6, có thêm Classes:
// class = function constructor