Node.js/Express를 현업에서 쓰진 않았지만 어떻게 사용하는지 궁금했다.
해서 Udacity강의를 들어보고 아래 유튜브를 보았다.
고백하자면 Clojure의 웹개발과 아주 비슷하다는 느낌을 받았다. 유튜브 강의에서 나오는 몇몇 기능은 개선을 하면 좀 더 멋지게(생산성있게) 개발할 수 있겠다는 생각도 했다.

var version = "3.4.1",
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
},
...
/*
제이쿼리 구조를 설명하기 위해 만든 간단한 함수
*/
(function (global) {
var mini = function(name) {
return new mini.fn.init(name);
}
mini.fn = mini.prototype = {};
mini.fn.sayHi = function() {
console.log("hi " + this.name);
return this;
}
mini.fn.sayBye = function() {
console.log("bye " + this.name);
return this;
}
mini.fn.init = function(name) {
this.name = name;
}
mini.fn.init.prototype = mini.fn;
global.mini = mini;
})(window);
var M = mini("Nam");
M.sayHi().sayBye();

var M = mini("nam");
M.sayHi().sayBye();
(define (cons a b)
(lambda (pick)
(cond ((= pick 1) a)
((= pick 2) b))))
(define (car x) (x 1))
(define (cdr x) (x 2))
;; 설명을 위해 더해진 것.
(define A (cons 1 2))
(print (car A))
(print (cdr A))
이 비디오를 보는 사람들 중 몇몇은 이미 이 내용을 설명할 때, 경악을 금치 못했다.(make-rat n d) ; rational-number (n,d의 저장형태는 알 필요없음) (numer rational-number) ; numerator 분자 (denom rational-number) ; denominator 분모여기서 make-rat는 constructor, numer/denom은 selector라고 불린다.
(define (+rat x y)
(make-rat
(+ (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
여기서 x,y는 rational-number이다. 우리는 x,y가 어떻게 구현되어 있는지는 모르지만, make-rat으로 유리수를 만들 수 있고, numer/denom으로 꺼낼 수 있는 것은 알고 있다.(define (*rat x y) (make-rat) (* (number x) (number y)) (* (denom x) (denom y)))자 이제 유리수를 구현해보자. 어떻게 담아야 할까?
(define (make-rat n d) (cons n d)) (define (numer x ) (car x)) (define (denom x) (cdr x))이렇게 cons를 이용하여 pair(리스트)를 만든다.
(define (cons a b)
(lambda (pick)
(cond ((= pick 1) a)
((= pick 2) b))))
(define (car x) (x 1))
(define (cdr x) (x 2))
여기에 데이터가 보이는가? 어떤 자료구조가 보이는가? So in some sense, we don't need data at all to build these data abstractions.
We can do everything in terms of procedures.
procedures are not just the act of doing something.
procedures are conceptual entities, objects.
function cons (a , b) {
return function(pick) {
switch(pick) {
case 1 :
return a;
case 2 :
return b;
default:
return;
}
}
}
function car(x) { return x(1); }
function cdr(x) { return x(2); }
var procedure = cons(10,20);
car(procedure);
cdr(procedure);
// 핸드폰 인풋 묶음 3개 유효성검사
var phone_1 = trim($("#phone_1").val());
var phone_2 = trim($("#phone_2").val());
var phone_3 = trim($("#phone_3").val());
if (phone_1 < 3 && phone_1 ...) { // 여러 and문과 유효성검사
$("#phone_1").focus();
alert("휴대폰 유효성검사실패");
return;
}
... phone_2, phone_3
$("#hidden_phone_").val(phone_1 + "-" phone_2 + "-" + phone_3);
이런 코드가 있다. 익숙하다. 일단 커리를 쓰기 전에
여기서 if문에 있는 모든 유효성검사는 하나의 함수로 추상화 해놓자.
if (validate(phone_1) { // 여러 and문과 유효성검사
$("#phone_1").focus();
alert("휴대폰 유효성검사실패");
return;
}
if (validate(phone_2) {
$("#phone_2").focus();
alert("휴대폰 유효성검사실패");
return;
}
if (validate(phone_3) {
$("#phone_3").focus();
alert("휴대폰 유효성검사실패");
return;
}
이런 코드를 본 적 없는가?
여기서 분명 우리는 뭔가 더 할 수 있을 것 같다. 어떻게 해야 할까? 맞다 객체를 넘기는 것이다.
function validateAndFocus(phone, $phone) {
if (!validatePhone(phone) {
alert("휴대폰 유효성검사 실패");
$phone.focus();
return false;
}
return true;
}
이제 이걸 사용해보자
if(!validateAndFocus(phone_1, $("#phone_1")) {
return;
}
if(!validateAndFocus(phone_2, $("#phone_2")) {
return;
}
if(!validateAndFocus(phone_3, $("#phone_3")) {
return;
}
...
뭔가 여기도 겹치는 것 같다.
if (!(validateAndFocus(phone_1, $("#phone_1")) &&
validateAndFocus(phone_2, $("#phone_2")) &&
validateAndFocus(phone_3, $("#phone_3"))) {
return;
}
undersocre.js의 and가 있다면 더 좋을 것 같다.
끝이 없을 것 같으니 여기서 넘어가자. underscore를 쓸 수 없어서 curry만 임시 사용하는 것이다.
이정도만 깔끔해보인다.
그런데 여기 주민번호와 카드번드까지 추가되었다.
var card1 = $("#card1");
var card2 = $("#card2");
var card3 = $("#card3");
var card4 = $("#card4");
var national_id_1 = $("#national_id");
var national_id_2 = $("#national_id");
우리는 validateAndFocus를 사용할 수 있는가?
사용할 수 없다.
1. 휴대폰번호의 유효성과 아이디의 유효성은 다르다. (predicate의 분리 필요)
2. 유효성 검사 이후 하는 일이 각각 다를 것이다.
하지만 형태는 동일하다.
1. 주어진 값의 유효성을 검사한다.
2. 통과하면 true 실패하면 false를 리턴한다.
3. 유효성검사에 실패하여 false를 리턴하기 전에 각각 해야하는 일이 있다.
// 동일한 형태의 일을 하는 함수 템플릿 정의
function validateTemplate(pred, func, obj1, obj2, obj3) {
if (pred(obj1, obj2, obj3)) {
func(obj1, obj2, obj3);
return false;
}
return true;
}
자 이 형태를 만들었다.
이제 어떻게 사용할지 보자. 유효성 검사를 분리해서 넣을 것이다.
그렇다면 이렇게 될 것이다.
var curried = curry(validateTemplate);
var validatePhoneCurried = curried(function(n) {
return validatePhone(n);
});
var validateCardCurried = curreid(function(n) {
return validatePhone(n);
});
var validateNationalidCurreid = curried(function(n) {
return validateNationalid(n);
});
뭐 대충 이렇게 코딩했다.
현재까지는 pred를 커리하고 이제는 각 pred마다 할일을 다르게 넣을 수 있겠다.
첫번째 인자를 _로 한 이유는 focus나 val("") 등 DOM조작을 위한 객체를 따로 넘긴다고 하자.
var validatePhoneFinal = validatePhoneCurried(function(_, $dom) {
alert("헨드폰문제!");
$dom.focus();
});
var validateCardFinal = validateCardCurried(function(_, $dom) {
alert("카드문제");
$dom.focus();
});
var validateNationalidFinal = validateNationalidCurreid (function(_, $dom) {
alert("주민번호문제");
$dom.focus();
});
이렇게 만들었다 치자. 이제 위에 코드를 저것들로 바꾸면 된다.
if (!(validatePhoneFinal(phone_1, $("#phone_1")) &&
validatePhoneFinal(phone_2, $("#phone_2")) &&
validatePhoneFinal(phone_3, $("#phone_3"))) {
return;
}
....
if (!(validateCardFinal(card_1, $("#card_1")) &&
validateCardFinal(card_2, $("#card_2")) &&
validateCardFinal(card_3, $("#card_3")) &&
validateCardFinal(card_4, $("#card_4"))) {
return;
}
...
if (!(validateNationalidFinal(national_id_1 , $("#national_id_1")) &&
validateNationalidFinal(national_id_2 , $("#national_id_2"))) {
return;
}
여기서 map이나 every 같은 것을 이용하는 것이 큰 도움이 될 것 같지만 그러진 않겠다. 오늘의 주제는 커리니까.function curry(fn) {
var arity = fn.length; // 함수의 필요 인자
// 매번 curry된 함수를 호출할 때마다 새로운 인자를 배열에 넣어 클로저 내에 저장한다.
// 배열의 길이는 fn.length와 동일해야 한다. (실행될 때)
// 혹여 인자의 수가 동일하지 않으면 새로운 함수로 반환한다.
// 인자 목록을 가지는 클로저가 필요하다 (함수로 둘러쌓아야 한다
// 또 여기서 개별의 클로저가 생성되야 하니까 즉시 실행함수로 만든다.)
// 전체인자(배열)과 fn.length를 확인
// 인자의 수가 부족하면 부분적으로 적용된 함수를 반환
// 인자의 수가 충족하면 fn에 모든 인자를 적용,호출하여 리턴
return (function resolver() {
// 지금까지 받은 인자를 복사한다. // 이전 클로저가 오염되지 말게
var memory = Array.prototype.slice.call(arguments);
// resolver는 익명함수를 반환한다.
// resolver는 인자가 부족할 때 반환한다.
// resolver가 새로 반환되는 이유는 클로저를 위한 것같다.
// resolver는 바로 실행되고 익명함수를 하나 리턴한다.
// resolver가 이전까지 모은 인자를 가지고 있다. (memory)
// 이걸 변수에 담았다가 나중에 실행시킬 것이다.
// 실행시키면 arguments에서 인자를 memory와 합체한다.
// 그리고 원래 실행되어야할 함수(fn)이 필요로 하는 인자의 갯수와 비교
// 지금까지 모은 인자(local)과 arity의 길이가 맞다면
// 원래함수를 호출(fn), 그렇지 않으면 resolver를 다시 반환 (인자를 더 받는다)
return function() {
var local = memory.slice();
Array.prototype.push.apply(local, arguments);
next = local.length >= arity ? fn : resolver;
return next.apply(null, local);
};
}());
}
function volume(l, w, h) {
return l * w * h;
}
var curried = curry(volume);
function curry(fn) {
var arity = fn.length; // 1. 숫자 3이 저장된다.
return (function resolver() {
var memory = Array.prototype.slice.call(arguments); // 2. resolver를 인자없이 실행하였다.(현재는 커리만 되는 상태)
return function() {
var local = memory.slice();
Array.prototype.push.apply(local, arguments);
next = local.length >= arity ? fn : resolver; // 3. arity가 부족하므로 함수를 리턴.
return next.apply(null, local);
};
}());
}
var length = curried(2);
function curry(fn) {
var arity = fn.length;
return (function resolver() {
var memory = Array.prototype.slice.call(arguments);
return function() { // 1. resolver로 반환된 익명함수가 실행됨.
var local = memory.slice(); // 2. memory는 현재 0개의 인자를 가지고 있다.
Array.prototype.push.apply(local, arguments); // 3.이번에 추가된 2가 local에 push된다.
next = local.length >= arity ? fn : resolver; // 4.아직 인자가 1개이기 때문에 다시 resolver를 반환한다.
return next.apply(null, local); // 4. 알다시피 이번에 던져지는 resolver는 또한 새로운 클로저와 함께하는 새로운 함수다.
};
}());
}
var lengthAndWidth = length( 3 );
function curry(fn) {
var arity = fn.length;
return (function resolver() {
var memory = Array.prototype.slice.call(arguments);
return function() {
var local = memory.slice(); // 1. 새로운 클로저에 들어있는 memory는 [2] 이다. local에 복사한다. (이전 클로저에 해를 끼치지 않게)
Array.prototype.push.apply(local, arguments); // 2. 새로운 인자 3을 추가한다. [2,3]
next = local.length >= arity ? fn : resolver; // 3.아직 인자가 2개이기 때문에 다시 resolver를 반환한다.
return next.apply(null, local); // 4. 알다시피 이번에 던져지는 resolver는 또한 새로운 클로저와 함께하는 새로운 함수다.
};
}());
}
console.log( lengthAndWidth( 4 ) ); // 24
function curry(fn) {
var arity = fn.length;
return (function resolver() {
var memory = Array.prototype.slice.call(arguments);
return function() {
var local = memory.slice(); // 1. 새로운 클로저에 들어있는 memory는 [2,3] 이다. local에 복사한다. (이전 클로저에 해를 끼치지 않게)
Array.prototype.push.apply(local, arguments); // 2. 새로운 인자 4을 추가한다. [2,3,4]
next = local.length >= arity ? fn : resolver; // 3.아직 인자가 3개이기 때문에 fn을 넣는다. (fn은 지금까지 변경된 적도 사용된 적도 있다. 하지만 이날을 위해 기다렸다)
return next.apply(null, local); // 4. 이번에는 함수를 던지지 않고 fn의 리턴값이 실행될 것이다. (하지만 fn이 리턴하는게 함수라면... 음...)
};
}());
}
(function () {
if(!Object.create) {
Object.create = (function () {
function F() {}
return function (o) {
F.prototype = o;
return new F();
}
})();
}
});
/*
class와 extends를 통한 상속
자바스크립트의 new기능의 모호함을 해소하기 위해
ECMA6에서는 class와 extends를 정의한다.
*/
class Person {
constructor() {
this.name = "anonymous";
}
}
class User extends Person {
constructor() {
super();
this.name = "User";
}
}
var user1 = new User();
console.log(user1 instanceof Person);
console.log(user1 instanceof User);
console.log(user1.constructor); // [Class: user]
/* Object.create와 new 키워드 조합
Object.create과 new 키워드를 조합하여 생성자의 연결이 깨지지 않도록 해보자.
*/
function Person() {
this.name = "unknown";
}
function User() {
this.name = "user";
}
// 두 번째 매개변수는 키는 속성명으로
// 새로 만든 객체에 추가될 속성 설명자(descriptor)를 지정합니다.
// writable option defaults to false.
// https://stackoverflow.com/questions/7757337/defining-read-only-properties-in-javascript
User.prototype = Object.create(Person.prototype, {
constructor: { // Person.prototype.constructor 는 이제 읽기전용이다.
value: User // 왜냐하면 기본이 writable = false 이다.
}
})
var myuser = new User();
console.log(myuser instanceof User);
console.log(myuser instanceof Person);
console.log(myuser.constructor);
이 녀석은 이전
function Car() {
this.wheel = 4;
this.beep = "BEEP";
}
Car.prototype.go = function () {
alert(this.beep);
};
function Truck() {
this.wheel = 6;
this.beep = "TRUCK!!";
}
Truck.prototype = new Car();
function SUV() {
this.beep = "SUV!!";
}
SUV.prototype = new Car();
var truck = new Truck(),
suv = new SUV();
console.log(truck.wheel); // 6
console.log(suv.wheel); // 4
console.log(truck.beep); // TRUCK!!
console.log(suv.beep); // SUV!!
Car라는 객체를 프로토타입에 넣어보니// 실제로는 Object.create 이라는 함수가 있다. 더글라스 크락포드가 주장했던 함수형태
Object.my_create = function(o) {
function F() {};
F.prototype = o; // O는 처음부터 prototype역할을 하는 녀석일 것이다.
return new F();
}
function Person(name) {
this.name = name;
}
Person.prototype = {
yell: function() {
alert("My name is " + this.name);
}
};
var u = Object.my_create(Person.prototype); // prototype을 넘기는 것을 주의하자.
u.name = "U";
u.yell(); // alert
/*
이렇게 Object.create 함수를 통해 객체를 생성하면
개발자가 new 키워드를 사용하지 않고 함수 호출로 객체가 생성된다.
new 키워드를 사용할 때와는 달리 전체적으로 소스에 생성자의 개념이 사라지고 (new가 없으니까)
객체의 인스턴스와 인스턴스 간의 상속을 강조하는 것이 Object.create의 특징 (???)
*/
function Person() {
this.name = "anomymous";
this.sayHello = function() {
console.log(this.name + "!!");
};
}
function Nam() {
this.name = "Nam";
}
Nam.prototype = new Person();
var nam = new Nam();
nam.sayHello();
console.log(nam instanceof Nam); // true
console.log(nam instanceof Person); // true
console.log(nam.constructor); // ?? Nam이 아니라 Person!!
/*
생성자 속성은 객체가 가지고 있는 것이 아니라. 프로토타입이 가지고 있다.
객체는 내부 링크로 프로토타엡에 있는 생성자 속성을 참조한다.
지금 Nam프로토타입은
그런데 new Person()으로 원래 객체(여기선 Nam)의 생성자를 가지고 있는 프로토타입을 덮어씌우면
Nam의 생성자는 연결고리를 잃는다. (원래 Nam의 생성자는 function Nam() {...} 을 가리키고 있어야 한다.)
따라서 nam.constructor를 실행하면 [Function: Person] 이 나온다.
뭐 잘 작동하기는 하지만 연결이 끊어진다는 것은 좋은 것이 아니다.
*/
function Person(name) {
this.name = name;
}
Person.prototype = {
yell: function() {
console.log("my name is " + this.name);
}
}
/* 수정불가 */
var user = Object.create(Person.prototype, {
name: {
value: 'new name'
}
})
/* 수정가능 */
user = Object.create(Person.prototype, {
name : {
value: 'new name2',
configurable: true,
enumerable: true,
writable: true
}
})
/* 접근자 활용의 예 */
Object.defineProperties(user, {
firstName: {
value: "Younghwan",
writable: true
},
lastName: {
value: "Yang",
writable: true
},
fullName: {
get: function() {
return this.firstName + " " + this.lastName;
},
set: function(value) {
var res = value.split(' ');
if (res.length > 1) {
this.firstName = res[0];
this.lastName = res[1];
} else {
console.log('wrong format');
}
}
});
console.log(user.fullName);
user.fullName = "Hello world!";
console.log(user.firstName); // Hello
console.log(user.lastName); // World!
왜냐하면 "무언가(config)와 좌표를 받아 좌표를 반환하는" 함수들을 결합할 경우,
"무언가" 부분을 부여해야 한다는 것이 조합할 때의 표현을 복잡하게 만들기 때문이라고 한다.
// 보다 나은 부품화
function clone(obj) {
var f = function(){};
f.prototype = obj;
return new f;
}
// 함수 합성
function compose (f,g) { return function(x) { return f(g(x)); } };
// 병행 이동의 프리미티브
//var trans = function (dx, dy, coord) {
// var result = clone(coord);
// result.x += dx;
// result.y += dy;
// return result;
//}
var trans = function (dx, dy, coord) {
return function(coord) {
var result = clone(coord);
result.x += dx;
result.y += dy;
return result;
}
}
// 회전 이동의 프리미티브
//var rotate = function (theta, coord) {
// var result = clone(coord);
// result.x = Math.cos(theta) * coord.x - Math.sin(theta) * coord.y;
// result.y = Math.sin(theta) * coord.x + Math.cos(theta) * coord.y;
// return result;
//}
var rotate = function(theta) {
return function(coord) {
var result = clone(coord);
result.x = Math.cos(theta) * coord.x - Math.sin(theta) * coord.y;
result.y = Math.sin(theta) * coord.x + Math.cos(theta) * coord.y;
return result;
}
}
// 설정을 베이스로 한 병행 이동
//var transByConfig = function (config, coord) {
// return trans(config.ofsX, config.ofsY, coord);
//}
var transByConfig = function(config) {
return trans(config.ofsX, config.ofsY);
}
// 설정을 베이스로 한 회전
//var rotateByConfig = function (config,coord) {
// var preTrans = trans(-config.rotAt.x, -config.rotAt.y, coord);
// var rotated = rotate(config.theta, preTrans);
// var postTrans = trans(config.rotAt.x, config.rotAt.y, rotated);
// return postTrans;
//}
var rotateByConfig = function(config) {
return compose(trans(config.rotAt.x, config.rotAt.y),
compose(rotate(config.theta),
trans(-config.rotAt.x, -config.rotAt.y)));
}
// 설정을 베이스로 한 좌표 변환
//var convertByConfig = function(config, coord) {
// return transByConfig(config, rotateByConfig(config, coord));
//}
var convertByConfig = function(config) {
return compose(transByConfig(config), rotateByConfig(config));
}
var config = {
rotAt : { x : 0.5, y : 0.5 }
, theta : Math.PI / 4
, ofsX : -0.5
, ofsY : -0.5
}
var unit_rect = [
{x:0,y:0}, {x:0,y:1}, {x:1,y:1}, {x:1,y:0}
]
// 변환 후의 좌표
//var converted_rect = unit_rect.map(function(coord) {
// return convertByConfig(config, coord);
//}
// 매개변수가 하나로 변하면서 map에서 바로 만들어진 함수를 지정할 수 있다. 하지만 curry를 이용해서 사용할 수도 있겠다.
var converted_rect = unit_rect.map(convertByConfig(config));
converted_rect.map(function(coord) {
console.log('(' + coord.x.toFixed(6) + ',' + coord.y.toFixed(6)+')');
}
함수 안에 있는 알고리즘을 보는 것이 아니라. return을 하는 방식이 변경되었고 compose를 이용한 함수합성을 유심히 봐야한다.
// 객체 복사용
function clone(obj) {
var f = function(){};
f.prototype = obj;
return new f;
}
// 병행 이동의 프리미티브
var trans = function (dx, dy, coord) {
var result = clone(coord);
result.x += dx;
result.y += dy;
return result;
}
var rotate = function (theta, coord) {
var result = clone(coord);
result.x = Math.cos(theta) * coord.x - Math.sin(theta) * coord.y;
result.y = Math.sin(theta) * coord.x + Math.cos(theta) * coord.y;
return result;
}
// 설정을 베이스로 한 병행 이동
var transByConfig = function (config, coord) {
return trans(config.ofsX, config.ofsY, coord);
}
// 설정을 베이스로 한 회전
var rotateByConfig = function (config,coord) {
var preTrans = trans(-config.rotAt.x, -config.rotAt.y, coord);
var rotated = rotate(config.theta, preTrans);
var postTrans = trans(config.rotAt.x, config.rotAt.y, rotated);
return postTrans;
}
// 설정을 베이스로 한 좌표 변환
var convertByConfig = function(config, coord) {
return transByConfig(config, rotateByConfig(config, coord));
}
var config = {
rotAt : { x : 0.5, y : 0.5 }
, theta : Math.PI / 4
, ofsX : -0.5
, ofsY : -0.5
}
var unit_rect = [
{x:0,y:0}, {x:0,y:1}, {x:1,y:1}, {x:1,y:0}
]
// 여기서 coord가 map으로 연결되서서 계속바뀌고
// config는 그대로 계속 공통으로 쓰게 되는 함수를 콜해서 리턴하는 익명함수를 각각 만들어서 실행한다.
var converted_rect = unit_rect.map(function(coord) {
return convertByConfig(config, coord);
});
converted_rect.map(function(coord) {
console.log('(' + coord.x.toFixed(6) + ',' + coord.y.toFixed(6)+')');
});
/// curry를 사용하면 좀 더 이뻐 보일 수 있겠다.
console.log('====');
var curriedConvertedByConfig = function(coord) {
return convertByConfig(config, coord);
}
var converted_rect2 = unit_rect.map(curriedConvertedByConfig);
converted_rect2.map(function(coord) {
console.log('(' + coord.x.toFixed(6) + ',' + coord.y.toFixed(6)+')');
});
위 내용을 html을 하나 생성해서 로드해보자. var ie = (function(){
if (window.ActiveXObject === undefined) return null;
if (!document.querySelector) return 7;
if (!document.addEventListener) return 8;
if (!window.atob) return 9;
if (!document.__proto__) return 10;
return 11;
})(); //ie버전체크
$('.evtPop').find('.img').removeClass(function (idx, className) {
console.log('클래스삭제', idx, className);
return className.match(/win\d/).join(' ');
})
function myAlert(index) {
$("#js-scope3-number"+index).click(function() {
alert("js-scope3-number" + index + " 번을 클릭했습니다.");
});
}
$(document).ready(function(){
var len = 3,
i;
for (i = 0; i < len; i++) {
myAlert(i);
}
});
보면 알 수 있겠지만 함수 안에서는 새로운 스코프가 연결된다.
다르게 해보자
$(document).ready(function(){
var len = 3,
i;
for (i = 0; i < len; i++) {
(function (index) {
$("#js-scope3-number"+index).click(function() {
alert("js-scope3-number" + index + " 번을 클릭했습니다.");
});
}(i));
}
});
관련페이지
http://www.whynam.com/2017/06/javascript.html
http://www.whynam.com/2017/06/javascript-2.html
var yhnam = {
name="yhnam"
blog="www.iamfrom.kr"
git="ssisksl77"
with (yhnam) {
console.log(name);
console.log(blog);
console.log(git);
}
실행안해봐도 대충 뭔지 알았을 것이다. 블록 안에 해당 객체가 가지고 있는 변수들을 추가한다.
이걸로 이용해서 문제를 해결하자.
var len = 3,
i;
for (i = 0; i < len; i++) {
with ({idx: i}){
$("js-scope2-number"+idx).click(function() {
alert("js-scope2-number" + idx + " 번을 클릭했습니다.");
}
}
}
하지만 이렇게 쓰지 말길.. ECMAScript에서 with쓰지말라고 아예 없애버린듯 하다.
관련페이지
http://www.whynam.com/search/label/javascript
http://www.whynam.com/2017/06/javascript-3.html