闭包: javascript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。
var foo = function(){
var name = "exe";
return function inner(){
console.log( name );
}
}
var bar = foo();
bar(); // exe
复制代码
出现这个状况的原因:
函数内部的[[scope]]是虚拟出来的一个属性,我们实际访问时是访问不到这个属性的,这个属性是为了让我们更好的理解函数,虚拟出来的一个内部属性。(我们不能使用,供js引擎使用) 我们在创建函数的时候会生成[[scope]]这个属性,这个属性就会保存函数被创建的作用域中对象的集合(这个集合呈链式链接,被称作函数的作用域链)。在函数执行时,函数会生成一个scope属性,这个属性保存着函数在执行上下文时创建的活动对象,活动对象包含函数内部的局部变量和函数参数,和函数内部的[[scope]]属性。
例子:
var x = 10;
function foo () {
console.log(x);
}
foo(); // 10
function fun () {
var x = 20;
var foo1 = foo;
foo1(); // 10还是20?
}
fun();
复制代码
最后的结果是10,原因:在复制函数的时候,复制后的函数与复制前的函数引用的是同一个[[scope]]属性,所以foo1执行的时候,foo1的作用域链就直接查找到了全局对象中的x属性,所以最后返回10
scope是域的意思
[[scope]]是在ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域对象的集合,这个集合被称为函数的作用域链,它决定哪些数据能被函数访问
1.在函数创建的时候,它的作用域链中会填入一个全局对象,该全局对象包含了所有的全局变量;
2.函数执行的时会创建一个成为“运行期上下文”的内部对象,运行期上下文定义了函数执行时的环境。每个运行期上下文都有自己的作用域链,用于标识符解析,当运行期上下文被创建时,它的作用域链初始化为当前运行函数的[[scope]]所包含的对象;
3.这些值按照它们出现在函数中的顺序被复制到运行期上下文的作用域链中,它们共同组成了一个新的对象,叫“活动对象”,该对象包含了函数的所有局部变量、参数命名、参数集合以及this,然后次对象会推入作用域链的前端
4.当运行期上下文被销毁,活动对象也随之销毁。
函数不能操作传入的参数: js里处理object是用引用传递,当调用函数时,如果修改object的值,是修改的引用的值(原值会被修改)
[[scope]]和执行上下文(执行环境)虽然保存的都是作用域链,但是不是同一个东西。 [[scope]]属性是函数创建时产生的,会一直存在 而执行上下文是在函数执行时产生的,函数执行结束便会销毁。
执行环境=执行上下文
套娃思维:blog.csdn.net/ChenXvYuan_…
js引擎查找作用域链是为了解析标志符,变量在执行环境作用域链的位置越深,读写速度就越慢。
在函数中读写局部变量总是最快的,读写全局变量通常最慢。
这些额外的性能开销对于优化js引擎来说可能微不足道,甚至可以说咩有性能损耗,但是还是要养成这样的编码习惯:尽量使用局部变量(变量) 例如:
function demo(){
var a = document.getElementById('a');
var b = document.getElementById('b');
var c = document.getElementById('c');
a.onclick = function(){
...
}
a.style.left = ...;
a.style.top = ...;
b.style.backgroundColor = ...;
c.className = ...;
document.body.appendChild(...);
document.body.appendChild(...);
document.body.appendChild(...);
}
复制代码
这段代码看起来缓存了,优化了代码,但是其实这段代码执行的过程中,js引擎解析标志符,要查找6次document,而且document位于window对象,也就是作用域链的最末尾(因为作用域链一层一层的网上找)
重构函数:
function demo(){
var doc = document,
bd = doc.body,
a = doc.getElementById('a'),
b = doc.getElementById('b'),
styleA = a.style;
a.onclick = function(){
...
}
styleA.left = ...;
styleA.top = ...;
styleA.backgroundColor = ...;
b.className = ...;
bd.appendChild(...);
bd.appendChild(...);
bd.appendChild(...);
}
复制代码
执行环境: 在函数执行时,会创建一个叫做执行环境/执行上下文的内部对象
它定义了一个函数执行时的环境
函数每次执行时的执行环境独一无二
多次调用函数就会多次创建执行环境
并且函数执行完毕后,执行环境就会被销毁
执行环境有自己的作用域链,用于解析标志符
C++/java面向对象,都是写一个class,然后new出相应的实例对象,如果class要继承另一个,直接用相应的语法继承就行了。 而在javascript中,采用的是原型继承的思路来实现面向对象的,通过new一个构造函数来生成一个对象
在javascript中,没有class的概念(ES6之后的语法糖暂且不提),而是使用一个函数,运行new的时候便会生成一个对象,有一个特别属性__proto__,它的值就是要new的对象的prototype的引用,新对象的__proto__ === 原对象的prototyoe。
每个函数都有一个prototype属性,
console.log(Person.prototype);
{constructor: ƒ}
constructor: ƒ Person(s)
__proto__: Object
复制代码
是一个包含constructor的对象,如果给这个prototype对象加点东西,那么由这个Person函数构造出来的对象就都能访问到。
Person.prototype = {
country: 'China'
};
复制代码
这时候
console.log(Person.prototype);
{country: "China"}
country: "China"
__proto__: Object
复制代码
每个构造函数都有一个prototype属性,该属性指向了一个原型对象
原型对象默认会有一个constructor属性指向的是prototype所在的函数(所以展开,永远都展不完) 函数.prototype === 对象.proto 就都可以访问到构造函数(构造函数中的prototype属性包含构造函数和__proto__,)
写csv文件时,return的语句块问题