+5

Javascript things

Chào mọi người !
Ngày nay Javascript trở nên rất phổ biến, đặc biệt trong việc xử lý các tương tác với client trên các trang web. Hôm nay chúng ta sẽ cùng tìm hiểu một số thứ "hay ho" của javascript nhé

🤧


I. argumentsthis

Mỗi khi 1 function được thực thi, 2 tham số ẩn được truyền vào: argumentsthis

1. arguments

Trong javascript, số lượng tham số truyền vào khi gọi hàm có thể khác với số lượng tham số (paramters) được khai báo của hàm, arguments lưu trữ tất cả những tham số được truyền khi gọi hàm:

function sum() {
    var sum = 0;
    for (var i = 0; i < arguments.length; i++){
        sum += arguments[i];
    }
    return sum;
}

sum(1, 2, 3, 4, 5);
=> 15

Một lưu ý rằng arguments là một alias tới các parameters của hàm. Nếu ta thay đổi giá trị của arguments, giá trị của parameters tưởng ứng sẽ thay đổi theo:

function func(name, sex) {
    arguments[0] = "SHERLOCK";
    console.log(name, sex);
}

func("HUYNV", "MALE");
=> SHERLOCK MALE

Ta có thể tránh việc ghi đè này với strict mode:

function func(name, sex) {
    "use strict";
    
    arguments[0] = "SHERLOCK";
    console.log(name, sex);
}

func("HUYNV", "MALE");
=> HUYNV MALE

2. this

Một tham số ẩn được truyền vào nữa, đó là this, hay còn được gọi : function context

function context là khái niệm trong các ngôn ngữ lập trình hướng đối tượng, trong đó, this trỏ tới object của class nới chứa method được gọi.

Tuy nhiên trong javascript, các function được thực thi bằng nhiều cách, và với mỗi cách đó, giá trị của tham số this cũng khác nhau


II. Invoking functions

Trong javascript, có 4 cách để thực thi một function:

1. Invoke as a function

function được gọi như function (???), đây là cách thức đơn giản nhất để thực thi một function

Với trường hợp này, this có thể nhận 2 giá trị, một là window object (không strict mode), hai là undefined (có strict mode):

function coder() {
    return this;
}

coder() === window;
=> true

function coder() {
    "use strict";
    
    return this;
}

coder() === undefined;
=> true

2. Invoke as a method

function có thể được gán cho 1 property của object, và được gọi như 1 method của object đó

Trường hợp này giống với các ngôn ngữ hướng đối tượng, this sẽ trỏ tới object gọi tới method đó:

var context = function() {
    return this;
}

context() === window;
=> true

var object = {
    getContext: context
}

object.getContext() === object;
=> true

3. Invoke as a constructor

Thực thi function như 1 constructor function với từ khóa new

Khi 1 function được gọi với new, 1 object rỗng được truyền vào và this sẽ lưu giá trị cho object mới này :

function Coder() {};

var coder = new Coder();

coder;
=> Coder {}__proto__: Object

Nếu trường hợp constructor function trả về một giá trị (?). Với trường hợp này, object được khởi tạo bằng việc Thực thi function với từ khóa new sẽ nhận giá trị là giá trị trả về nếu đó là 1 object, còn lại các giá trị trả về khác (như số, chuỗi, mảng, ...) thì sẽ nhận giá trị là this:

obj = {
    name: "OBJECT000"
}

function Coder() {
    return 0;
}

var coder000 = new Coder();

coder000 === obj;
=> false

function Coder() {
    return obj;
}

var coder001 = new Coder();

coder001 == obj;
=> true

4. Invoke with call and apply

Từ 3 trường hợp trên ta có thể thấy function context (hay this) chỉ có thể nhận giá trị là window hoặc là object gọi method đó.

Trong nhiều trường hợp, ta muốn function context là 1 giá trị theo ý muốn, javascript hỗ trợ 2 method callapply cho điều này :

function setLever(lever) {
    if (lever > 0) {
        this.lever = "Intern";
    } else {
        this.lever = "Open";
    }    
}

var coder001 = {};
var coder002 = {};

setLever.call(coder001, 1);
setLever.call(coder002, 0);

coder001
=> {lever: "Intern"}
coder002
=> {lever: "Open"}

Điểm khác nhau giữa callapply là các tham số của hàm khi sử dụng call được truyền trực tiếp ngay sau object muốn gán, còn đối với apply thì các tham số đó phải truyền vào qua các phần tử của một mảng.


III. bind

Nếu đã nói tới applycall, chắc hẳn ta đã nghe ở đâu đó về method bind trong javascript

Điểm khác biệt giữa bind với apply hay call đó là với apply hay call, hàm sẽ được thực thi ngay lập tức với function context được truyền vào, còn bind sẽ tạo ra một function mới, với nội dung giống với function ban đầu, và context của nó luôn là 1 object được truyền vào trong tham số khi dùng bind.

Vì thế mà bind thường được sử dụng làm các hàm callback.

Ví dụ dưới đây sẽ xử lý tình huống user click button chọn file, sau đó không chọn file mà lại ấn cancel. Với trường hợp user nhấn cancel này, ta muốn ảnh đã được user chọn trước đó sẽ bị ẩn đi :

$(document).on('click', 'input[type="file"]', function(){
    var checkCancelClicked = function(){
      if(this.val() == ''){
        $('.preview-img').addClass('hidden');
        $('.original-img').removeClass('hidden');
        document.body.onfocus = null;
      }
    }
    document.body.onfocus = checkCancelClicked.bind($(this));
  });

checkCancelClicked.bind($(this)); sẽ tạo mới 1 hàm dùng làm callback cho sự kiện focus, với function context được gán là $(this) chính là input[type="file"].

Một câu hỏi khác, nếu ta không sử dụng bind cho checkCancelClicked mà chỉ có :

 document.body.onfocus = checkCancelClicked;

thì giá trị this trong function checkCancelClicked là gì ?


IV. Closure

1. Scope

Trong javascript:

Scope là khả năng truy cập, sử dụng các biến, hàm, object trong một phần code cụ thể

Scope trong javascript có loại : global scopelocal scope . Những biến được định nghĩa trong một function được sẽ nằm trong local scope, những biến định nghĩa bên ngoài function nằm trong global scope. Mỗi một function khi được thực thi sẽ tạo ra một scope mới.

1.1. Globle scope

Chỉ có duy nhất 1 global scope xuyên suốt toàn bộ file javascript. Khi bắt đầu viết code, ta đã ở trong global scope. Những gì được định nghĩa ở đây có thể được truy cập sử dụng ở mọi scope khác

var globalScope = 'GLOBAL SCOPE';
// globleScope có thể truy cập ở mọi nơi

function func() {
    console.log(globalScope); // globleScope có thể truy cập ở mọi nơi
}

func();
=> "GLOBAL SCOPE"

1.2. Local scope

Các biến được định nghĩa trong function, chỉ có thể sử dụng trong phạm vi local scope của chúng

// Global Scope
function someFunction() {
    // Local Scope #1
    function someOtherFunction() {
        // Local Scope #2
    }
}

// Global Scope
function anotherFunction() {
    // Local Scope #3
}
// Global Scope

Block statement: các câu lệnh điều kiện if, switch hay vòng lặp for, loop không giống như function (tạo ra scope mới), những biến được định nghĩa trong các block statement vẫn nằm trong scope mà chúng thuộc về trước đó :

// Global Scope
if (true) {
    // this 'if' conditional block doesn't create a new scope
    var name = 'Sherlock'; // name is still in the global scope
}

console.log(name);
=> "Sherlock"

2. Closure

closure được định nghĩa :

Closure cho phép 1 function có thể truy cập và sử dụng được các biến ở bên ngoài function đó

var outerValue = "samurai";
var later;

function outerFunction(){
    var innerValue = "ninja";

    function innerFunction(){
        console.log(outerValue, innerValue);
    }

    later = innerFunction;
}

outerFunction();

later();
=> samurai, ninja

Trong ví dụ trên, khi later() được thực thi ở global scope, theo lý thuyết ở trên thì nó không thể sử dụng được những biến local scope được khai báo trong function, tuy nhiên kết quả lại cho thấy điều ngược lại.

Điều này được thực hiện bởi closure, 1 safe bubble được tạo ra, chứa toàn bộ các biến, function có trong scope tại thời điểm function đó được định nghĩa, và safe bubble này tồn tại đến khi nào function còn tồn tại


Trên đây là một số tìm hiểu của mình về javascripts, cảm ơn mọi người đã đọc bài viết 🤧


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí