自调用函数
var name = 'world!'; // console.log(typeof name) (function () { console.log(this.name, name, typeof name, typeof name === undefined) if(typeof name === undefined){ var name = 'a!' console.log('hello ' + name) } else{ var name = 'b!' console.log('hello ' + name) } })();
运行结果是
"world!" undefined "undefined" false
hello b!
这里有两个陷阱
第一个是变量提升后 name 值为 undefined
第二个是 typeof name 为字符串 "undefined"
闭包
闭包就是能读取其他函数内部变量的函数。
优点:
保护函数内的变量安全,加强了封装性
在内存中维持一个变量(用的太多就变成了缺点,占内存)
缺点:
闭包有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅因为它常驻内存,更重要的是,对闭包的使用不当会造成无效内存的产生
var counter = 2 var add = (function () { console.log(this.counter, counter, 'bbb') // 2, undefined, 'bbb' var counter = 3; return function () { counter += 1; console.log(counter) } })(); add(); // 4 add(); // 5 add(); // 6 console.log(counter, 'aaa') // 2, 'aaa'
原型链
所有对象的 __proto__ 都指向其构造器的 prototype
所有函数对象的 __proto__ 都指向 Function.prototype,它是一个空函数(Empty function)
Object.prototype.__proto__ === null
function fn() { this.a = 10 this.b = function () { console.log(this.a) } } fn.prototype = { b: function () { this.a = 20 console.log(this.a) }, c: function () { this.a = 30 console.log(this.a) } } var myfn = new fn(); console.log(myfn) //对象属性有a,b;原型链继承属性有b,c
console.log(myfn.__proto__ === fn.prototype) // true
console.log(myfn.__proto__ === myfn.constructor.prototype) // false
myfn.b() //10
myfn.c() //30
myfn.b() //30
解析
myfn对象第一次调用b方法,由于原型链上的b方法被覆盖,所以始终调用自身的b方法,所以就输出初始值10
myfn调用c方法等于是调用继承的c方法,所以a属性被重新赋值,于是输出30
myfn第二次调用b方法,由于a属性已经被c方法重新赋值,所以继续输出30
myfn.__proto__始终指向构造器fn的prototype属性
但是myfn.__proto__不再指向myfn.constructor.prototype
myfn本身没有constructor属性,于是指向fn.prototype.constructor属性,
而fn.prototype.constructor本来应该指向其自身,但是因为重新赋值fn.prototype对象,所以fn.prototype的构造函数变成了Object
引用指针
var a = {n:1}; var b = a; a.x = a = {n:2}; console.log(a.x); // undefined console.log(b.x); // {n:2}
解释
这里的连续赋值运算顺序是先进行'.'运算符赋值,这时将a.x还是指向对象A的,
接下来开始从右向左赋值,改变a的指向为对象B
接下来开始a.x的赋值为对象B,这时候'.'运算符已经最先赋值了,所以还是给对象A的x属性赋值