5. JavaScript Function Class

김관주·2023년 10월 11일
0

웹시스템

목록 보기
5/22

Functions

  • A function is a self-contained collection of statements that run as a single unit
    함수는 하나의 단위로 실행되는 자체적인 문장 모음입니다
  • A function declaration
    • Define a body of function, which is the collection of statements

Function Declaration

// 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);
}

Invoking (calling, executing, running) Functions

  • Function invocation
    • let total = distance(0,0,2,1) + distance(2,1,3,5);
  • Method invocation
    • Method: function stored in an object
    • If you have a function f and an object o, you can define a method named m of o with the following line:
      • o.m = f;
      • Invoking: o.m();
const o = {
name: ‘Wallace’,
bark: function() {return ‘Woof’; }
}
// we can do this in ES6
const o = {
name: ‘Wallace’,
bark() {return ‘Woof’; }
} 

Return Values

  • The return keyword will immediately terminate the function and return the specified value, which is what the function call will resolve to.
    • Calling a function is an expression and expressions resolve to a value
  • Functions that are going to return a value must use the return statement.
    • Otherwise, the return value will be undefined
function prod(a,b)
{ x=a*b;
return x; } 

Calling vs. Referencing

  • In JavaScript, functions are objects (first class)
    • can be passed around and assigned just like any other object.
  • calling a function
    • follow a function identifier with parentheses(괄호)
    • JavaScript knows that you’re calling it: it executes the body of the function, and the expression resolves to the return value.
  • referencing a function
    • don’t provide the parentheses(괄호)
    • you’re simply referring to the function just like any other value
const f = getGreeting; // referencing a function
f();
const g = getGreeting(); // calling a function
console.log(g);

Function Arguments

  • The primary mechanism to pass information to a function call
    • Also called parameters
    • Arguments are like variables that don’t exist until the function is called.
  • Formal arguments
    • Arguments in a function declaration (i.e., a and b)
function prod(a,b)
{ x=a*b;
return x; }
  • Actual arguments
    • When a function is called, formal arguments receive values and become actual arguments (i.e., the values 2 and 3)
product=prod(2,3); 
  • The arguments exist only in the function

매개변수(parameter)는 함수 안에서의 정의 및 사용에 나열되어 있는 변수들을 의미하고요. 전달 인자(argument)는 함수를 호출할 때 전달되는 실제 값을 의미해요

this keyword

  • Inside a function body, a special read-only value called this is available
    • The this keyword relates to functions that are properties of objects.
    • When methods are called, the this keyword takes on the value of the specific object it was called on:
const o = {
name: ‘GwanJu’,
speak() {return (‘My name is ’ + this.name); }
} 
o.speak(); // My name is GwanJu

Function Expressions and Anonymous Functions

  • Function expressions are syntactically identical to function declarations except that you can omit the function name.
  • A function expression is simply a way to declare a (possibly unnamed) function. A function expression can be assigned to something (thereby giving it an identifier) or called immediately.
  • The example
    • use a function expression and assign the result to a variable
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

Arrow function (fat arrow notation =>)

  • Arrow functions are always anonymous
  • Simplify function in syntax in three ways:
  1. You can omit the word function.
const f1 = function() { return "hello!"; }
// OR
const f1 = () => "hello!";
  1. If the function takes a single argument, you can omit the parentheses.
const f2 = function(name) { return (‘Hello,+ name); }
// OR
const f2 = name => ‘Hello,+ name;
  1. If the function body is a single expression, you can omit curly braces(중괄호 { }) and the return statement
const 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 and Import

  • Export (declaration): used to export values (functions, variables, class) from a JavaScript module
  • Import (declaration): can be used to import values from another program

a.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() {}
}

Objects

  • Like arrays, objects in JavaScript are containers (also called aggregate or complex data types). Objects have two primary differences from arrays:
    • 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.
  • A property consists of a key (a string or symbol) and a value

Object-Oriented Programming

  • JavaScript is an Object Oriented Programming (OOP) language.

    An OOP language allows you to define your own objects and make your own variable types.

  • An object is just a special kind of data. An object has properties and methods.
  • More precisely, an object is an unordered collection of properties, each of which has a name (or a key) and a value. Property names are strings, so we can say that objects map strings to values.
    • 자바스크립트 객체는 '프로토타입'이라고 하는 다른 객체의 속성도 상속하는데, 객체의 메소드는 일반적으로 상속되는 속성이며, 이 '프로토타입 상속'은 자바스크립트의 주요 특징입니다.

Class and Instance Creation

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;
    }
}
  • Template strings
    • Template strings (literals) delimited with backtick (`) characters, allowing for multi-line strings, string interpolation with embedded expressions, and special constructs called tagged templates.
console.log(`string text ${expression} string text`);

Class with inheritance

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, Property and Method (ECMA 2016)

class Person {
    gender = 'male';
    printGender =() => {
        console.log(this.gender);
    }
}
class Student extends Person {
    name = 'Sangyoon';
    printName = () => {
        console.log(this.name);
    }
}

생성자 선언을 단축하고, arrow function을 사용하였다.

Class are Functions

  • Classes are just Functions. In ES5, we would have started the Car class as follows
function Car(make, model) {
this.make = make;
this.model = model;
this._userGears = ['P', 'N', 'R', 'D];
this._userGear = this.userGears[0];
}
  • Still a class is a function in ES6

class Es6Car {}
-> typeof Es6Car // function
function Es5Car() {}
-> typeof Es5Car // function

Scope

  • Scope determines when and where variables, constants, and arguments are
    considered to be defined.
  • Scope vs. Existence
    • variables and constants do not exist before we create them
    • Scope (or visibility) refers to the identifiers that are currently visible and accessible by the currently executing part of the program (called the execution context).
    • Existence, on the other hand, refers to identifiers that hold something for which memory has been allocated.

Lexical (Static) scoping

함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수의 호출이 아니라 함수의 선언에 따라 결정된다는 점이다.

  • Scoping in JavaScript is lexical

    • we can determine what variables are in scope simply by looking at the source code.
    • However, it is not to say that scope is always immediately obvious from the source code.
  • 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)를 따르므로 함수를 선언한 시점에 상위 스코프가 결정된다.
즉, 이 말은 함수를 어디에서 호출하였는지는 스코프 결정에 아무런 의미를 주지 않는다는 말이다.

  • Lexical scoping in JavaScript applies to global scope, block scope, and function scope.

Global Scope

  • Anything you declare in global scope will be available to all scopes in your program
  • The scope that you’re implicitly in when you start a JavaScript program.
  • Globals (declared in global scope)
    • is not bad, It is necessary necessary
    • But, the over-use or abuse of global scope is bad
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;
}

Block Scope

  • A block is a list of statements surrounded by curly braces.{ }
  • Block scope refers to identifiers that are only in scope within the block.
  • let and const declare identifiers in block scope.

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

Variable Masking (variable shadowing)

{ // 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
}

Functions, Closure, and Lexical Scope

  • A closure is to intentionally define a function in a specific scope so that it explicitly has access to that scope. <- called 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에 접근 가능하다.
  • When we call globalFunc, it has access to blockVar despite the fact that we’ve exited that scope

closure의 올바른 정의
❗️함수와 함수가 선언된 어휘적(lexical) 환경의 조합
클로저는 함수를 지칭하고 또 그 함수가 선언된 환경과의 관계라는 개념이 합쳐진것이다.

❗️클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(폐쇄)것에 있다.

  • 외부함수 스코프에서 내부함수 스코프로 접근 불가능하다.
  • 내부함수에서는 외부함수 스코프에서 선언된 변수에 접근 기능하다.
  • 따라서 내부 함수는 외부함수에 선언된 변수에 접근 가능하다.

❗️함수가 호출되는 환경과 별개로, 기존에 선언되어 있던 환경(어휘적 환경)을 기준으로 변수를 조회한다.

  • 외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있습니다.
  • 외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있다.
  • 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있습니다.

상위 스코프의 식별자를 포함하여 쓰여있는 내부 함수 코드 자체를 어휘적 환경(lexical environment)라고 부를 수 있다

  • 내부함수(innerFn)가 상위 스코프(예:outerFn)의 식별자를 참조하고 있고, 그 내부함수(innerFn)를 그 상위 스코프(예:outerFn) 바깥에서 사용했을 때, 그 상위 스코프(예:outerFn)의 식별자를 수정할 수 없는 형태이다.
  • 즉, 함수가 정의되었던 위치에서 상위 스코프(예:outerFn)식별자를 기억하고 있는 형태이다.
  • 그래서 중첩함수 뿐만 아니라 내부함수가 외부 스코프에 접근할 수 있는 형태의 클로저도 존재한다

클로저를 만드는 형태 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;

IIFE (Immediately invoked function expression)

  • Declares a function and runs it immediately

(function() { // this is the IIFE body })();

  • The variable secret is safe inside the scope of the IIFE, and can’t be accessed from outside.
const message = (function() {
    const secret = "I'm a secret!";
    return 'What is the secret? ' + secret;
})();
console.log(message);
  • a function that can report the number of times it’s been called in a way that can’t be tampered with:
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)."
//...

클로저와 IIFE 차이?

https://dev-dain.tistory.com/125

Function Scope and Hoisting

  • var variable declaration
    • ES6에서 let을 도입하기 전 변수는 var(global scope)를 사용하여 선언되었습니다
  • function scope: 변수는 정의된 함수 내에서 볼 수 있고 해당 함수 내에 중첩된 함수 내에서 볼 수 있습니다.
    • There was No block scope prior to ES6
  • JavaScript Hoisting은 코드를 실행하기 전에 interpreter가 함수, 변수 또는 클래스의 선언을 범위 상단으로 이동하는 것처럼 보이는 과정을 말합니다.
    • only the declaration—not the assignment—is hoisted

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 Hoisting

호이스팅 예제 (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;
};

Strict Mode for Scope

  • ES5 allowed implicit globals
    • if you forgot to declare a variable with var, JavaScript would merrily assume you were referring to a global variable.
    • If no such global variable existed, it would create one!
  • Strict Mode
    • prevent implicit globals
    • "use strict“ or ‘use strict’
function() {
'use strict';
// code…
}

TDZ (Temporal Dead Zone)

  • 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/

Exception and Error Handling

Exception Handling with try and catch

  • You can also “throw” (or “raise”) errors yourself, which initiates the exception handling mechanism.
  • In JavaScript, you can throw any value: a number or a string, or any other type. However, it’s conventional to throw an instance of Error.
function billPay(amount, payee, account) {
    if(amount > account.balance)
        throw new Error("insufficient funds");
    account.transfer(payee, amount);
}
  • When you call throw, the current function immediately stops executing.

Exception Handling with call stack

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...catch…finally

  • Whether or not there is an error, we want to free the resource so that it’s not permanently tied up by your program.
  • The finally block, which gets called whether or not there is an error.
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");
}

0개의 댓글