获取页面元素的位置方式
通过累加offset的方式来获取坐标
//取得元素x坐标
function pageX(elem) {
return elem.offsetParent?(elem.offsetLeft+pageX(elem.offsetParent)):elem.offsetLeft;
}
复制代码
- 通过最新的函数getBoundingClientRect()直接获取left,top,bottom,right位置
函数原型与原型链
function Person() {
}
let person = new Person
复制代码
实例person对象通过__proto__ 获取实例原型或者通过Object.getProtoTypeOf() 最终找寻到null,形成一条完整的原型链
person.__proto__ = Person.protoType
Person.protoType.__proto__ = Object.protoType
Object.protoType.__proto__ = null
复制代码
函数通过prototype获取到原型 原型通过contructor获取到构造函数
Person.protoType.contructor = Person
复制代码
使用原型的方式挂载函数,并不会增加实例负担,生成的实例上并没有对应的函数,仅仅使用的时候会关联到原型上,不用而外为每个实例开辟对应的函数存储空间。
闭包与执行上下文的关系
function bar() {
var a = 10
function foo() {
console.log(a)
}
return foo
}
bar()()
复制代码
执行上下文操作:
全局上下文压栈
stack= [globalContext]
bar 压栈
stack = [ globalContext, barContext ]
bar 退栈
stack= [ globlcontext ]
foo 压栈
stack = [
globalContext,
foo
]
foo 退栈 输出a
stack= [ globalContext ]
全局上下文退栈 stack=[],执行结束
执行期间,虽然bar中变量a被foo持有,从而延长到foo退栈才销毁,但是bar上下文的生存时间并没有延长,在执行完成后便直接退栈
连等赋值问题
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
a.x // 这时 a.x 的值是多少
b.x // 这时 b.x 的值是多少
复制代码
正确的答案是a.x结果为undefined,b.x结果是{n:2}
首先:
var a, b
a = b = {n:1}
a // {n:1}
b // {n:1}
复制代码
证明连等操作执行从右至左
原因:在执行连等操作的时候,会开启一份副本来执行赋值操作
//a.x=a={n:2} 从运算符优先级判断 先执行a.x ,再从右到左执行=
a.x // a.x 中的a地址被单独记录,即可以看做{n:1}.x
a = {n:2} // 将a指向{n:2},a.x中的a仍然是{n:1}
a.x = a// 修改a后,a.x内存地址已经被记录副本中,因此a.x中的a地址仍旧是{n:1}对象地址,不受影响,可以看成{n:1}.x={n:2}
a // {n:2}
b // {n:1,x:{n:2}},b仍然指向{n:1}
复制代码
垃圾回收
现在浏览器一般不再使用引用计数的方式判断对象是否应该回收,避免了循环引用造成的内存泄露。使用标记清除的方式来判断,从根部出发定时扫描对象,那些无法达到的对象标记为不再使用,清除。
从ES5规范判定this指向
在规范中有一种只存在于规范的类型Reference,只存在于规范中的抽象类型,不存在于实际的js中。 Reference由三部分组成:
-base value
-reference name
-strict reference
base value 指的是属性所在对象或者环境记录,值可以是undefined,object,Boolean,string,number,environmentRecord, reference name指属性名称。
var foo = 1
// 对应的Reference是:
var fooReference = {
base: EnvironmentRecord,
name: 'foo',
strict: false
};
var bar = {
fn: funtion() {
return this
}
}
// fn对应的Reference是:
var BarReference = {
base: bar,
propertyName: 'fn',
strict: false
};
复制代码
规范中有两种获取reference组成的方法,GetBase和IsPropertyReference,GetBase可以获得base value内容,IsPropertyReference可以用来判断reference中的base value是否为一个对象。 GetValue则是一个通过reference类型获取其真实内容的函数,返回值是一个具体的值,不再是reference类型
var foo = 1
// 对应的Reference是:
var fooReference = {
base: EnvironmentRecord,
name: 'foo',
strict: false
};
GetValue(fooReference) // 1
复制代码
通过规范确定this可以通过这几步:
1.计算MemberExpression的结果给ref,memberExpression的表现形式
-PrimaryExpression // 原始表达式
-FunctionExpression // 函数表达式
-MemberExpression[Expression] // 属性访问
-MemberExpression.IdentifierName // 属性访问点语法
-new MemberExpression Arguments // 对象创建表达式
也可以简单说是()左边的部分,例如foo()中的foo或者foo.bar()中的foo.bar
2.判断ref是否是一个Reference类型
3.1 如果IsPropertyReference(ref)是true,那么this的值为GetBase(ref),也就是ref的base value
3.2 如果base value是一个environment Record,那么this的值为 ImplicitTHisvalue(ref),ImpliciThisValue值恒为undefined
3.3 如果不是Reference,那么this的值为undefined
案例分析:
var value = 1;
var foo = {
value: 2,
bar: function () {
return this.value;
}
}
//示例1
console.log(foo.bar());
//示例2
console.log((foo.bar)());
//示例3
console.log((foo.bar = foo.bar)());
//示例4
console.log((false || foo.bar)());
//示例5
console.log((foo.bar, foo.bar)());
复制代码
示例1:
memberExpression是foo.bar,属性访问表达式,bar的Reference:
var Reference = {
base: foo,
name: 'bar',
strict: false
};
复制代码
IsPropertyReference(ref)是true,this是foo,所以输出2
示例2:
(foo.bar)跟foo.bar一样
(foo.bar = foo.bar)()
看示例3,有赋值操作符,查看规范 11.13.1 Simple Assignment ( = ):
计算的第三步:
3.Let rval be GetValue(rref).
因为使用了 GetValue,所以返回的值不是 Reference 类型,
按照之前讲的判断逻辑:
2.3 如果 ref 不是Reference,那么 this 的值为 undefined
this 为 undefined,非严格模式下,this 的值为 undefined 的时候,其值会被隐式转换为全局对象。
(false || foo.bar)()
看示例4,逻辑与算法,查看规范 11.11 Binary Logical Operators:
计算第二步:
2.Let lval be GetValue(lref).
因为使用了 GetValue,所以返回的不是 Reference 类型,this 为 undefined
(foo.bar, foo.bar)()
看示例5,逗号操作符,查看规范11.14 Comma Operator ( , )
计算第二步:
2.Call GetValue(lref).
因为使用了 GetValue,所以返回的不是 Reference 类型,this 为 undefined
复制代码
示例:
foo(),memberExpression是foo,reference为:
var Reference = {
baseValue:environmentRecord
name:foo
strict:false
}
复制代码
根据3.2,如果返回environmentRecord,使用impliciThisValue返回undefined,非严格指window
CONST
const保证的并不是变量值不能改动,而是变量所指的内存地址保存的数据不得改动。
对应基本类型,值就保存在变量指向的地址中,因此此时变量内容等同常量,不可修改。但是对于对象,变量保存地址仅仅是指向数据的指针,const仅仅保证了指针不可变动,内容数据改变不能控制