from:http://weizhifeng.net/javascript-the-core.htm
1.对象
①一个对象就是一个属性集合,并拥有独立的prototyoe(原型)对象,这个prototype可以是一个对象或者null。
②一个对象的prototype是通过内部的proto属性来引用的。
2.原型链
①原型对象(如person prototype),也是简单的对象,并且可以拥有它们自己的原型。(对象的prototype也有自己的prototype原型,通过前者内部的proto)。
②原型链是一个用来实现继承和共享属性的有限对象链。
③想象这么一种情况,我们拥有两个对象,两者绝大部分都一致,只有一些很小的差别。为了省事,或者说的好听点,为了节约系统内存或提高运行效率,希望将其相同的部分合并,个性区域分为两个小块,但是,ES(ESCAScript)没有类这个概念。这个时候原型链就派上用场了,也就引出了原型继承。但是呢,原型继承共享性又太强了,没有了隐私的存在。因此又诞生了一个新的东西—-组合继承(原型链+借用结构函数)和寄生组合式继承(前面两个再加个寄生继承)。
var a={
x:10,
calculate:function(z){
return this.x+this.y+z;
}
};
var b={
y:20,
__proto__:a
};
var c={
y:30,
__proto__:a
};
b.calculate(5);//35=10+20+5
c.calculate(9);//49=10+30+9
3.构造函数
/*
A constructor function which may create objects by specified
pattern:they have after creation own 'y' property
*/
function Foo(y){
this.y=y;
}
/*
also 'Foo.prototype' stores reference to the prototype of
newly created objects,so we may use it to define shared/
inherited properties or methods,so the same as in previous
example we have:
*/
//inherited property
Foo.prototype.x=10;
//inherited method
Foo.prototype.calculate=function(z){
return this.x+this.y+z;
};
//now creata our 'b' and 'c' objects using pattern Foo
var b=new Foo(20);
var c=new Foo(30);
//call the inherited method
b.calculate(30);//60=10+20+30
c.calculate(40);//80=10+30+40
b.__proto__===Foo.prototype;//true
c.__proto__===Foo.prototype;//true
b.constructor===Foo;//true
c.constructor===Foo;//true
b.x;//10
c.x;//10
Foo.prototype.constructor===Foo;//true
b.calculate===b.__proto__.calculate;//true
b.__proto__.calculate===Foo.prototype.calculate;//true
Foo.__proto__===Function.prototype;//true
Foo.prototype===Foo.prototype;//true
Foo.prototype.__proto__===Object.prototype;//true
Function.prototype.__proto__===Object.prototype;//true
Object.prototype.__proto__===null;//true
①以指定模式创建对象/自动为创建的对象设置一个原型对象。这个原型对象存储在ConstructorFunction.prototype属性中。
②每一个对象都有一个原型。
a)构造函数Foo也有自己的proto,值为Function.prototype
b)Function.prototype也有自己的proto,值为Object.prototype
c)Object.prototype也有自己的proto,值为null
③ES中凭借构造函数和原型对象的组合,媲美“类”。
4.执行上下文栈
①三种类型的ES代码:全局代码,函数代码,eval代码。
②栈中第一个元素(最下面的)永远全局上下文(Global execution context)
5.执行上下文
①一个execution context可以抽象看成一个对象。
②该“对象”包含三个必需属性:variable object(变量对象),this,scope chain
③
var foo=10;
function fd(){}//function declaration,FD
(function fe(){});//function expressions,FE
this.foo===foo;//true
window.fd===fd;//true
window.fe===fe;//fe is not defined
6.变量对象(activation object)
①变量对象是执行上下文相关的数据作用域,它是一个与上下文相关的特殊对象,其中储存了在上下文中定义的变量和函数声明(函数表达式不在该对象中)。
Object.prototype.x=10;
var w=20;
var y=30;
x;//10
//(函数)();-----函数表达式
(function foo(){
var w=40;
var x=100;
with({z:50}){
console.log(w,x,y,z);//40 10 30 50
}
console.log(x,w);//100 40
console.log(window.w);//20
console.log(this.w);//20
})();
②只有函数可以创建一个新的作用域。在函数作用域中所定义的变量和内部函数在函数外部是无法直接访问到的,而且并不会污染全局变量对象。
③在函数的上下文中,变量对象是以活动对象(activation object)来表示的。
7.活动对象
①当一个函数被调用时,会创建一个特殊的对象,叫做活动对象。
②这个对象中包含形参和一个特殊的arguments对象(是形参的一个映射,但值是通过索引获取的)。
③活动对象之后会成为函数上下文中的变量对象来使用。
④ES5中的变量对象和活动对象被并入词法环境模型(lexical environment model)。
8.作用域链
①作用域链是一个对象列表,上下文代码中出现的标识符在这个列表中寻找。
②查找规则:如果一个变量在该函数自身作用域(变量/活动对象)中没有找到,那么会继续在其父函数(外层函数)查找,以此类推。
③如果在一个函数中引用了一个不是局部变量(或局部函数或形参)的标识符,该标识符叫做自由变量。搜索这些自由变量时需要用到作用域链。
④当原型链与作用域链相结合时,先proto,后parent。
⑤全局变量内部的proto属性指向Object.prototype.【不是在所有实现中全局对象都继承自Object.prototype】
9.闭包
①当函数作为参数传递给其他函数时,这个参数叫做函数类型参数,funargs—functional arguments,接收函数类型参数的函数叫做高阶函数。
②同样,函数也可以从其他函数中返回,返回其他函数的函数叫做以函数为值的函数(functions with functional value)。【函数类型值】
③scope chain(作用域链)=Activation object+[[scope]]
④确切得来说是:在创建时刻,函数b会保存父函数a的作用域链。
⑤
function foo(){
var x=10;
return function bar(){
console.log(x);
};
}
var returnedFunction=foo();
var x=20;
returnedFunction();//10
//自上而下
var x=10;
function foo(){
console.log(x);
}
(function(funArg){
var x=20;
funArg();
})(foo);//10
⑥闭包的精确定义:闭包是一个代码块,在ES中就是一个函数,以静态方式存储着包括父作用域链中的活动对象(变量对象)。通过这些存储的作用域,便于查找自由变量。
⑦多个函数可能共享父作用域,比如在函数a中,嵌套了多个函数。导致一个闭包对变量的修改会体现在另一个闭包对变量的读取上。
unction baz(){
var x=1;
return{
foo:function foo(){return ++x;},
bar:function bar(){return --x;}
};
}
var closures=baz();
closures.foo();//2
closures.foo();//3
closures.foo();//4
closures.bar();//3
closures.bar();//2
⑧
var data=[];
for(var k=0;k<3;k++){
data[k]=function(){
console.log(k);
};
}
data[0]();//3
data[1]();//3
data[2]();//3
个人的一种解释:基于闭包共享+变量提升—-因为JS中没有块级作用域这个概念,只有全局作用域和局部作用域,再加个eval,而局部作用域是由定义函数时创建的,该代码中for循环的变量k为全局变量,其内部的多个函数(数组函数)形成闭包,因此全局变量(Global Varibale object)中的k=3,而每个数组函数中指向的k都是全局变量中的那个k=3,因此所有输出均为3.
一种解决办法:通过函数表达式为每个循环创建一个新作用域,并在新作用域中用一个新变量保存每次循环里k的值,这些新作用域被独立保存在每个数组元素的函数的闭包里,互不影响。
var data=[];
for(var k=0;k<3;k++){
data[k]=(function help(x){
return function(){
console.log(x);
}
})(k);
}
data[0]();//0
data[1]();//1
data[2]();//2
var data=[];
for(var j=0;j<3;j++){
data[j]=(function help(j){
return function(){
console.log(j);
};
})(j);
}
data[0]();//0
data[1]();//1
data[2]();//2
⑨为什么var 换成let就变成0 1 2 了—当前let声明的变量仅仅在本轮循环中有效。
使用let来声明变量,类似于var,但是所声明的变量,仅仅在let命令所执行的代码块中有效。
var data=[];
for(let k=0;k<3;k++){
data[k]=function(){
console.log(k);
};
}
data[0]();//0
data[1]();//1
data[2]();//2
for(let i=0;i<3;i++){
let i='yyc';
console.log(i);
}
//yyc
//yyc
//yyc
10.
/*
foo() returns also a function and this returned
function uses free variable 'x'
*/
function foo(){
var x=10;
return function bar(){
console.log(x);
};
}
foo();//返回的是foo函数中return的那个bar函数。
bar();
/*
Uncaught ReferenceError: bar is not defined。同时这个时候你在外部
是无法访问到foo函数内部的bar函数的。
*/
var result=foo();
/*
foo()表示它的foo()函数返回值(函数类型值),放在result这个变量中保存。
*/
result();//10
/*
result()表示,执行了bar函数,在该函数内部的变量x是自由变量。
*/
11.了解一种新的写法,省事—(funtion(){})();匿名的,可以自动执行的一种函数的表现方式。分号前的圆括号里代表匿名函数的参数。