// Compute the distance between Cartesian points (x1,y1) and (x2,y2).
function distance(x1, y1, x2, y2) {
let dx = x2 - x1;
let dy = y2 - y1;
return Math.sqrt(dx*dx + dy*dy);
}
// A recursive function (one that calls itself) that computes factorials
// Recall that x! is the product of x and all positive integers less than it.
function factorial(x) {
if (x <= 1) return 1;
return x * factorial(x-1);
}
f
and an object o
, you can define a method named m of o with the following line:o.m
= f
;o.m()
;const o = {
name: ‘Wallace’,
bark: function() {return ‘Woof’; }
}
// we can do this in ES6
const o = {
name: ‘Wallace’,
bark() {return ‘Woof’; }
}
return
keyword will immediately terminate the function and return the specified value, which is what the function call will resolve to.function prod(a,b)
{ x=a*b;
return x; }
const f = getGreeting; // referencing a function
f();
const g = getGreeting(); // calling a function
console.log(g);
function prod(a,b)
{ x=a*b;
return x; }
product=prod(2,3);
매개변수(parameter)는 함수 안에서의 정의 및 사용에 나열되어 있는 변수들을 의미하고요. 전달 인자(argument)는 함수를 호출할 때 전달되는 실제 값을 의미해요
const o = {
name: ‘GwanJu’,
speak() {return (‘My name is ’ + this.name); }
}
o.speak(); // My name is GwanJu
let distance = function (x1, y1, x2, y2) {
let dx = x2 - x1;
let dy = y2 - y1;
return Math.sqrt(dx*dx + dy*dy);
};
let factorial = function (x) {
if (x <= 1) return 1;
return x * factorial(x-1);
};
나중에 호출할 명명 함수를 정의할 때 -> function declaration
어떤 것에 할당하거나 다른 함수에 전달하기 위해 함수를 만들어야 할 경우 -> function expression
const f1 = function() { return "hello!"; }
// OR
const f1 = () => "hello!";
const f2 = function(name) { return (‘Hello, ’ + name); }
// OR
const f2 = name => ‘Hello, ’ + name;
{ }
) and the return statementconst f3 = function(a, b) { return a + b; }
// OR
const f3 = (a,b) => a + b;
또 다른 예제들..
// expression at the right side
let sum = (a, b) => a + b; // 1, 3
// or multi-line syntax with { ... },
// need return here:
let sum = (a, b) => { // 1
// ...
return a + b;
}
// without arguments
let sayHi = () => alert("Hello");// 1, 3
// with a single argument
let double = n => n * 2; // 1, 2, 3
Export
(declaration): used to export values (functions, variables, class) from a JavaScript moduleImport
(declaration): can be used to import values from another programa.js
(default export)
function namePrint () {
console.log(`Sangyoon`);
}
export default namePrint;
JavaScript에서는 export default 뒤에 하나의 값 또는 객체만을 지정할 수 있습니다. 여러 개의 변수나 값들을 동시에 export default로 지정하는 것은 허용되지 않습니다.
b.js
(named export)
let myVariable = Math.sqrt(2);
const foo = Math.PI + 8;
export {myVariable, foo};
main.js
import nPrint from './a.js';
import {myVariable} from './b.js';
import {foo as mFoo} from './b.js';
named export와는 다르게 default export는 가져오기 할 때 개발자가 원하는 대로 이름을 지정해 줄 수 있습니다.
export default class { // 클래스 이름이 없음
constructor() { ... }
}
export default function(user) { // 함수 이름이 없음
alert(`Hello, ${user}!`);
}
// 이름 없이 배열 형태의 값을 내보냄
export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
export default는 파일당 하나만 있으므로 이 개체를 가져오기 하려는 모듈에선 중괄호 없이도 어떤 개체를 가지고 올지 정확히 알 수 있으므로 이름이 없어도 괜찮습니다
default를 붙이지 않았다면 개체에 이름이 없는 경우 에러가 발생합니다.
export class { // 에러! (default export가 아닌 경우엔 이름이 꼭 필요합니다.)
constructor() {}
}
Array
에는 숫자로 인덱싱된 값이 포함되어 있고 Object
에는 문자열 또는 기호로 인덱싱된 속성이 포함되어 있습니다.Arrays
are ordered (arr[0] always comes before arr[1]); objects
are not (you can’t guarantee obj.a comes before obj.b) 순서 보장 X.JavaScript
is an Object Oriented Programming (OOP) language. An OOP language allows you to define your own objects and make your own variable types.
class Car {
constructor() {
}
}
const car1 = new Car();
const car2 = new Car();
car1 instanceof Car // true
car1 instanceof Array // false
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
this.userGears = ['P', 'N', 'R', 'D'];
this.userGear = this.userGears[0];
}
shift(gear) {
if(this.userGears.indexOf(gear) < 0)
throw new Error(`Invalid gear: ${gear}`);
this.userGear = gear;
}
}
console.log(`string text ${expression} string text`);
class Person {
constructor() {
this.gender = ‘male’;
}
printGender() {
console.log(this.gender);
}
}
class Student extends Person {
constructor() {
super();
this.name = ‘Sangyoon’;
}
printName() {
console.log(this.name);
}
}
const student = new Student();
student.printName(); // will print ‘Sangyoon’
student.printGender(); // will print ‘male’
class Person {
gender = 'male';
printGender =() => {
console.log(this.gender);
}
}
class Student extends Person {
name = 'Sangyoon';
printName = () => {
console.log(this.name);
}
}
생성자 선언을 단축하고, arrow function을 사용하였다.
function Car(make, model) {
this.make = make;
this.model = model;
this._userGears = ['P', 'N', 'R', 'D’];
this._userGear = this.userGears[0];
}
class Es6Car {}
-> typeof Es6Car // function
function Es5Car() {}
-> typeof Es5Car // function
함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수의 호출이 아니라 함수의 선언에 따라 결정된다는 점이다.
Scoping in JavaScript is lexical
Lexical scoping means whatever variables are in scope where you define a function from (as opposed to when you call it) are in scope in the function
'Lexical scoping'은 함수의 범위에서 함수를 정의할 때 범위 내에 있는 모든 변수를 의미합니다
자바스크립트는 렉시컬 스코프(Lexical Scope)를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.
즉, 이 말은 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다는 말이다.
let name = "Irena"; // global
let age = 25; // global
function greet() {
console.log('Hello', + name);
}
function getBirthYear() {
return new Date().getFullYear() - age;
}
let user = {
name : "Irena",
age : 25,
};
function greet() {
console.log('Hello', + user.name);
}
function getBirthYear() {
return new Date().getFullYear() - user.age;
}
{ }
var은 block scope 적용 안된다!!
let으로 선언된 변수는 해당 블록 내에서만 사용가능합니다.
console.log(`before block`);
{
console.log(`inside block`);
const x = 3;
console.log(x); // logs 3
}
console.log(`outside block; x= ` + x); // ReferenceError: x is not defined
{ // outer block
let x = { color: "blue" };
let y = x; // y and x refer to the same object
let z = 3;
{ // inner block
let x = 5; // outer x now masked
console.log(x); // logs 5
console.log(y.color); // logs "blue"; object pointed to by
// y (and x in the outer scope) is
// still in scope
y.color = "red";
console.log(z); // logs 3; z has not been masked
}
console.log(x.color); // logs "red"; object modified in inner scope
console.log(y.color); // logs "red"; x and y point to the same object
console.log(z); // logs 3
}
closure
let globalFunc; // undefined global function
{
let blockVar = 'a'; // block-scoped variable
globalFunc = function() {
console.log(blockVar);
}
}
globalFunc(); // logs "a"
globalFunc
는 블록 내의 값에 할당됩니다. 해당 블록(and its parent scope, the global scope)은 closure를 형성합니다. 어디에서 globalFunc
를 호출하든 해당 closure의 identifier에 접근 가능하다.closure의 올바른 정의
❗️함수와 함수가 선언된 어휘적(lexical) 환경의 조합
클로저는 함수를 지칭하고 또 그 함수가 선언된 환경과의 관계라는 개념이 합쳐진것이다.
❗️클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(폐쇄)것에 있다.
❗️함수가 호출되는 환경과 별개로, 기존에 선언되어 있던 환경(어휘적 환경)을 기준으로 변수를 조회한다.
상위 스코프의 식별자를 포함하여 쓰여있는 내부 함수 코드 자체를 어휘적 환경(lexical environment)라고 부를 수 있다
클로저를 만드는 형태 1. - 중첩함수
function outerFn() {
let x = 10;
return function innerFn(y) { // innerFn 함수는 클로저다.
return x = x + y;
}
}
let a = outerFn(); // 외부함수 호출은 한번만. 이제 a 변수는 innerFn 함수를 참조한다.
a(5); // 15;
a(5); // 20;
a(5); // 25;
클로저를 만드는 형태 2. - 전역에 선언한 변수를 박스 안에서 함수로 정의하고 전역에서 호출
let globalFunc;
{
let x = 10;
globalFunc = function(y) { // globalFunc 함수는 클로저다.
return x = x + y;
}
}
globalFunc(5); // 15;
globalFunc(5); // 20;
globalFunc(5); // 25;
(function() { // this is the IIFE body })();
const message = (function() {
const secret = "I'm a secret!";
return 'What is the secret? ' + secret;
})();
console.log(message);
const f = (function() {
let count = 0;
return function() {
++count;
return ('I have been called ' + count + 'time(s).');
}
})();
f(); // "I have been called 1 time(s)."
f(); // "I have been called 2 time(s)."
//...
https://dev-dain.tistory.com/125
var
variable declarationJavaScript Hoisting
은 코드를 실행하기 전에 interpreter
가 함수, 변수 또는 클래스의 선언을 범위 상단으로 이동하는 것처럼 보이는 과정을 말합니다.let은 참조 에러가 일어난다. 마치 호이스팅이 되지 않는 것처럼. 하지만 사실 호이스팅은 된다. 아래에서 자세한 내용을 다루겠다.
x; // ReferenceError: x is not defined
let x = 3; // we'll never get here -- the error stops execution
var은 전역변수이므로 호이스팅 된다. 단, 선언만 되고 할당이 되지 않는다는 것을 기억하자.
x; // undefined
var x = 3;
x; // 3
var
로 선언된 변수의scope
는
현재 실행 컨텍스트,
해당 실행 컨텍스트 내에 선언된 함수,
그 내부에서 선언된 함수들,
함수 외부에서 선언된 변수의 경우
전역(global)입니다.
호이스팅 예제 (function declaration)
var result = add(5, 5);
function add (x, y) {
return x + y;
}
오류 예제 (function expression)
// error. because a function expression is not hoisted
var result = add(5, 5);
var add = function (x, y) {
return x + y;
};
function() {
'use strict';
// code…
}
Variables declared with let don’t exist until you declare them.
Checking whether or not variables are defined with typeof will be less necessary in ES6, so in practice, the behavior of typeof in the TDZ should not cause a problem.
ECMAScript 6 (ES6) 및 이후 버전의 JavaScript에서 typeof의 동작은 let 및 const 선언의 도입으로 인해 변수가 정의되었는지 확인하는 필요성을 줄일 수 있으므로 TDZ에서 typeof의 동작이 문제가 될 가능성이 적어질 것입니다.
{ // TDZ starts at beginning of scope
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2; // End of TDZ (for foo)
}
console.log(typeof i);
let i = 10;
javascript에서의 변수는 위의 사진처럼 선언, 초기화, 할당이라는 3가지 단계의 걸쳐서 생성됩니다.
var 키워드 변수는 변수 선언전에 선언 단계와 초기화 단계를 동시에 진행합니다.
그래서 javascript는 실행 컨텍스트 변수 객체의 변수를 등록하고 메모리를 undefined로 만들어 버립니다. 그렇기 때문에 변수를 선언하기 전에 호출을 해도 undefined로 호출이 되는 호이스팅이 발생하는 것 입니다
let으로 선언된 변수는 var 키워드와는 다르게 선언단계와 초기화 단계가 분리되어서 진행이 됩니다. 그렇기 때문에 실행 컨텍스트에 변수를 등록했지만, 메모리가 할당이 되질 않아 접근할 수 없어 참조 에러(ReferenceError)가 발생하는 것 이고, 이것을 보고 우리가 호이스팅이 되질 않는다고 오해할 수 밖에 없었던 것 입니다.
TDZ는 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말합니다.
즉, let 또한 선언전, 실행 컨텍스트 변수 객체에 등록이 되어 호이스팅이 되지만,
이 TDZ 구간에 의해 메모리가 할당이 되질 않아 참조 에러(ReferenceError) 발생하는 것 입니다.
헷갈리는 호이스팅 링크 https://hanamon.kr/javascript-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85%EC%9D%B4%EB%9E%80-hoisting/
function billPay(amount, payee, account) {
if(amount > account.balance)
throw new Error("insufficient funds");
account.transfer(payee, amount);
}
function a() {
console.log('a: calling b');
b();
console.log('a: done');
}
function b() {
console.log('b: calling c');
c();
console.log('b: done');
}
function c() {
console.log('c: throwing error');
throw new Error('c error');
console.log('c: done');
}
function d() {
console.log('d: calling c');
c();
console.log('d: done');
}
try {
a();
} catch(err) {
console.log(err.stack);
}
try {
d();
} catch(err) {
console.log(err.stack);
}
try {
console.log("this line is executed...");
throw new Error("whoops");
console.log("this line is not...");
} catch(err) {
console.log("there was an error...");
} finally {
console.log("...always executed");
console.log("perform cleanup here");
}