>  기사  >  웹 프론트엔드  >  JavaScript로 마스터해야 할 10가지 어려운 점(반드시 읽어야 함)

JavaScript로 마스터해야 할 10가지 어려운 점(반드시 읽어야 함)

黄舟
黄舟원래의
2018-05-11 11:13:517191검색

아직 모르는 JavaScript의 10가지 어려움, 걱정하지 마세요. 이 글에는 누구나 하나씩 나열되어 있으니 관심 있는 친구들이 참고하면 됩니다.

이 블로그를 읽을 수 있는 JavaScript 개발자 , 행운을 빕니다...

1. 함수 즉시 실행

함수 즉시 실행, 즉 IIFE(Immediate Invoked Function Expression)는 이름에서 알 수 있듯이 함수가 생성되면 즉시 실행합니다. 이벤트를 바인딩하지 않으며 비동기 작업을 기다릴 필요가 없습니다.

(function() {
 // 代码
 // ...
})();

function(){…}은 익명 함수이며 이를 둘러싼 괄호 쌍은 이를 표현식으로 변환하고 그 뒤에 괄호 쌍이 옵니다. 함수가 호출됩니다. 함수를 즉시 실행한다는 것은 익명 함수를 즉시 호출하는 것으로 이해될 수도 있습니다. 함수를 즉시 실행하는 가장 일반적인 애플리케이션 시나리오는 이름 충돌을 피하기 위해 var 변수의 범위를 함수로 제한하는 것입니다.

2. 클로저

클로저의 경우 외부 함수가 반환된 후에도 내부 함수는 여전히 외부 함수의 변수에 액세스할 수 있습니다.

function f1()
{
 var N = 0; // N是f1函数的局部变量
 function f2() // f2是f1函数的内部函数,是闭包
 {
 N += 1; // 内部函数f2中使用了外部函数f1中的变量N
 console.log(N);
 }
 return f2;
}
var result = f1();
result(); // 输出1
result(); // 输出2
result(); // 输出3

코드에서 외부 함수 f1은 한 번만 실행되고 변수 N은 0으로 설정되며 내부 함수 f2는 변수 result에 할당됩니다. 외부 함수 f1이 실행되었으므로 내부 변수 N이 메모리에서 지워져야 합니다. 그러나 그렇지 않습니다. result를 호출할 때마다 변수 N이 메모리에 있었고 누적되고 있음을 알 수 있습니다. 왜? 이것이 클로저의 마법입니다!

3. 클로저를 사용하여 개인 변수 정의

일반적으로 JavaScript 개발자는 개인 변수의 접두어로 밑줄을 사용합니다. 그러나 실제로 이러한 변수는 계속 액세스하고 수정할 수 있으며 진정한 개인용 변수는 아닙니다. 이때 클로저를 사용하여 실제 프라이빗 변수를 정의할 수 있습니다.

function Product() {
 var name;
 this.setName = function(value) {
 name = value;
 };
 this.getName = function() {
 return name;
 };
}
var p = new Product();
p.setName("Fundebug");
console.log(p.name); // 输出undefined
console.log(p.getName()); // 输出Fundebug

코드에서 객체 p의 name 속성은 프라이빗 속성이므로 p.name을 사용하여 직접 접근할 수 없습니다.

4.prototype

각 JavaScript 생성자에는 모든 인스턴스 객체가 공유해야 하는 속성과 메서드를 설정하는 데 사용되는 프로토타입 속성이 있습니다. 프로토타입 속성을 열거할 수 없습니다. JavaScript는 프로토타입 속성을 통한 속성 및 메서드 상속만 지원합니다.

function Rectangle(x, y)
{
 this._length = x;
 this._breadth = y;
}
Rectangle.prototype.getDimensions = function()
{
 return {
 length: this._length,
 breadth: this._breadth
 };
};
var x = new Rectangle(3, 4);
var y = new Rectangle(4, 3);
console.log(x.getDimensions()); // { length: 3, breadth: 4 }
console.log(y.getDimensions()); // { length: 4, breadth: 3 }

코드에서 x와 y는 모두 Rectangle 생성자에 의해 생성된 객체 인스턴스이며 프로토타입을 통해 getDimensions 메서드를 상속합니다.

5. 모듈성

JavaScript는 적어도 ES6가 구현되기 전까지는 모듈식 프로그래밍 언어가 아닙니다. 그러나 복잡한 웹 애플리케이션의 경우 모듈식 프로그래밍이 가장 기본적인 요구 사항입니다. 이때 jQuery, Fundebug 등 많은 JS 라이브러리가 이러한 방식으로 구현되는 것처럼 즉시 실행 기능을 사용하여 모듈성을 달성할 수 있습니다.

var module = (function() {
 var N = 5;
 function print(x) {
 console.log("The result is: " + x);
 }
 function add(a) {
 var x = a + N;
 print(x);
 }
 return {
 description: "This is description",
 add: add
 };
})();
console.log(module.description); // 输出"this is description" 
module.add(5); // 输出“The result is: 10”

소위 모듈화란 필요에 따라, 즉 프라이빗 또는 퍼블릭에 따라 모듈의 속성과 메서드에 대한 접근성을 제어하는 ​​것입니다. 코드에서 module은 독립 모듈이고, N은 전용 속성, print는 전용 메서드, 설명은 공용 속성, add는 공용 메서드입니다.

6. 변수 호이스팅

JavaScript는 모든 변수 및 함수 선언을 범위 앞으로 이동합니다. 이를 변수 호이스팅(호이스팅)이라고 합니다. 즉, 변수와 함수를 어디에 선언하든 인터프리터는 이를 범위 앞쪽으로 이동합니다. 따라서 변수와 함수를 먼저 사용한 다음 선언할 수 있습니다.

단, 변수 선언만 승격되고 변수 할당은 승격되지 않습니다. 이것을 이해하지 못하면 때로는 문제가 발생합니다.

console.log(y); // 输出undefined
y = 2; // 初始化y

위 코드는 다음 코드와 동일합니다.

var y; // 声明y
console.log(y); // 输出undefined
y = 2; // 初始化y

버그를 방지하려면 개발자는 각 범위의 시작 부분에 변수와 함수를 선언해야 합니다.

7. Currying

Currying, 즉 Currying을 사용하면 함수를 더 유연하게 만들 수 있습니다. 한 번에 여러 매개변수를 전달하여 호출할 수 있으며, 매개변수 중 일부만 전달하여 호출하고 나머지 매개변수를 처리하는 함수를 반환하도록 할 수도 있습니다.

var add = function(x) {
 return function(y) {
 return x + y;
 };
};
console.log(add(1)(1)); // 输出2
var add1 = add(1);
console.log(add1(1)); // 输出2
var add10 = add(10);
console.log(add10(1)); // 输出11

코드에서는 두 개의 1을 매개변수로 add(1)(1)에 한 번에 전달할 수도 있고, 매개변수 1개를 전달한 다음 add1 및 add10 함수를 얻을 수도 있는데 이는 매우 유연하게 사용할 수 있습니다.

8. 적용, 호출 및 바인드 메소드

JavaScript 개발자는 적용, 호출 및 바인드 메소드의 차이점을 이해해야 합니다. 이들의 공통점은 첫 번째 매개변수가 this라는 것입니다. 이는 함수가 실행될 때 의존하는 컨텍스트입니다.

세 가지 중에서 호출 방법이 가장 간단합니다.

var user = {
 name: "Rahul Mhatre",
 whatIsYourName: function() {
 console.log(this.name);
 }
};
user.whatIsYourName(); // 输出"Rahul Mhatre",
var user2 = {
 name: "Neha Sampat"
};
user.whatIsYourName.call(user2); // 输出"Neha Sampat"

적용 방법은 호출 방법과 유사합니다. 둘 사이의 유일한 차이점은 Apply 메서드는 배열을 사용하여 매개변수를 지정하는 반면 호출 메서드는 각 매개변수를 개별적으로 지정해야 한다는 것입니다.

apply(thisArg, [argsArray])
call(thisArg, arg1, arg2, …)
var user = {
 greet: "Hello!",
 greetUser: function(userName) {
 console.log(this.greet + " " + userName);
 }
};
var greet1 = {
 greet: "Hola"
};
user.greetUser.call(greet1, "Rahul"); // 输出"Hola Rahul"
user.greetUser.apply(greet1, ["Rahul"]); // 输出"Hola Rahul"

bind 메서드를 사용하면 이 값을 함수에 바인딩한 다음 반환할 수 있습니다. 새로운 기능:

var user = {
 greet: "Hello!",
 greetUser: function(userName) {
 console.log(this.greet + " " + userName);
 }
};
var greetHola = user.greetUser.bind({greet: "Hola"});
var greetBonjour = user.greetUser.bind({greet: "Bonjour"});
greetHola("Rahul") // 输出"Hola Rahul"
greetBonjour("Rahul") // 输出"Bonjour Rahul"

9. Memoization

Memoization은 계산 결과를 메모리에 캐시하여 시간이 많이 걸리는 계산을 최적화하는 데 사용되므로 동일한 입력 값에 대해 결과만 읽으면 됩니다. 기억은 다음번에.

function memoizeFunction(func)
{
 var cache = {};
 return function()
 {
 var key = arguments[0];
 if (cache[key])
 {
  return cache[key];
 }
 else
 {
  var val = func.apply(this, arguments);
  cache[key] = val;
  return val;
 }
 };
}
var fibonacci = memoizeFunction(function(n)
{
 return (n === 0 || n === 1) ? n : fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(100)); // 输出354224848179262000000
console.log(fibonacci(100)); // 输出354224848179262000000

코드에서 fibonacci(100)의 두 번째 계산은 메모리에서 직접 결과를 읽기만 하면 됩니다.

10. 함수 오버로딩

所谓函数重载(method overloading),就是函数名称一样,但是输入输出不一样。或者说,允许某个函数有各种不同输入,根据不同的输入,返回不同的结果。凭直觉,函数重载可以通过if…else或者switch实现,这就不去管它了。jQuery之父John Resig提出了一个非常巧(bian)妙(tai)的方法,利用了闭包。

从效果上来说,people对象的find方法允许3种不同的输入: 0个参数时,返回所有人名;1个参数时,根据firstName查找人名并返回;2个参数时,根据完整的名称查找人名并返回。

难点在于,people.find只能绑定一个函数,那它为何可以处理3种不同的输入呢?它不可能同时绑定3个函数find0,find1与find2啊!这里的关键在于old属性。

由addMethod函数的调用顺序可知,people.find最终绑定的是find2函数。然而,在绑定find2时,old为find1;同理,绑定find1时,old为find0。3个函数find0,find1与find2就这样通过闭包链接起来了。

根据addMethod的逻辑,当f.length与arguments.length不匹配时,就会去调用old,直到匹配为止。

function addMethod(object, name, f)
{  
 var old = object[name];  
 object[name] = function()
 {
 // f.length为函数定义时的参数个数
 // arguments.length为函数调用时的参数个数    
 if (f.length === arguments.length)
 {  
  return f.apply(this, arguments);    
 }
 else if (typeof old === "function")
 {
  return old.apply(this, arguments);    
 }  
 };
}
// 不传参数时,返回所有name
function find0()
{  
 return this.names;
}
// 传一个参数时,返回firstName匹配的name
function find1(firstName)
{  
 var result = [];  
 for (var i = 0; i < this.names.length; i++)
 {    
 if (this.names[i].indexOf(firstName) === 0)
 {      
  result.push(this.names[i]);    
 }  
 }  
 return result;
}
// 传两个参数时,返回firstName和lastName都匹配的name
function find2(firstName, lastName)
{ 
 var result = [];  
 for (var i = 0; i < this.names.length; i++)
 {    
 if (this.names[i] === (firstName + " " + lastName))
 {      
  result.push(this.names[i]);    
 }  
 }  
 return result;
}
var people = {  
 names: ["Dean Edwards", "Alex Russell", "Dean Tom"]
};
addMethod(people, "find", find0);
addMethod(people, "find", find1);
addMethod(people, "find", find2);
console.log(people.find()); // 输出["Dean Edwards", "Alex Russell", "Dean Tom"]
console.log(people.find("Dean")); // 输出["Dean Edwards", "Dean Tom"]
console.log(people.find("Dean", "Edwards")); // 输出["Dean Edwards"]

위 내용은 JavaScript로 마스터해야 할 10가지 어려운 점(반드시 읽어야 함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.