作用域(Scope)
1. 什么是作用域
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。可能这两句话并不好理解,我们先来看个例子:
function outFun2() {
var inVariable = "内层变量2";
}
outFun2();//要先执行这个函数,否则根本不知道里面是啥
console.log(inVariable);
从上面的例子可以体会到作用域的概念,变量 inVariable 在全局作用域没有声明,所以在全局作用域下取值会报错。我们可以这样理解:作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
2.全局作用域和函数作用域
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
最外层函数 和在最外层函数外面定义的变量拥有全局作用域
var outVariable = "我是最外层变量"; //最外层变量
function outFun() { //最外层函数
var inVariable = "内层变量";
function innerFun() { //内层函数
console.log(inVariable);
}
innerFun();
}
console.log(outVariable); //我是最外层变量
outFun(); //内层变量
console.log(inVariable); //inVariable is not defined
innerFun(); //innerFun is not defined
所有末定义直接赋值的变量自动声明为拥有全局作用域
function outFun2() {
variable = "未定义直接赋值的变量";
var inVariable2 = "内层变量2";
}
outFun2();//要先执行这个函数,否则根本不知道里面是啥
console.log(variable); //未定义直接赋值的变量
console.log(inVariable2); //inVariable2 is not defined
所有 window 对象的属性拥有全局作用域
般情况下,window 对象的内置属性都拥有全局作用域,例如 window.name、window.location、window.top 等等。
原型
JavaScript 的每个对象都继承另一个父级对象,父级对象称为原型 (prototype)对象。
原型也是一个对象,原型对象上的所有属性和方法,都能被子对象 (派生对象) 共享 通过构造函数生成实例对象 时,会自动为实例对象分配原型对象。 而每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。
var Person = function(msg){
this.msg = msg;
}
var person1 = new Person("wanger")
person1.constructor===Person; //true
Person === Person.prototype.constructor; //true
person1.__proto__ === Person.prototype; //true
person1.__proto__.constructor === person1.constructor //true
原型链
在JavaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接。这个原型对象又有自己的原型,直到某个对象的原型为 null 为止(也就是不再有原型指向),组成这条链的最后一环。这种一级一级的链结构就称为原型链(prototype chain)。
JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
var o = {
a:1,
b:2
};
console.log(o.toString());
//不报错,o上没有toString方法,但是Object上有
console.log(o.push("c"));
//报错,o上没有这个方法,Object上也没有这个方法。
console.log(o.a); //1
console.log(o.c); //undefined
当你用new Object或者直接定义一个对象时,它的原型链就是:o ==》 Object.prototype ==》 null
但你访问o上没有的属性或方法时,JS会往Object.prototype上寻找该属性和方法。如果有则直接返回,如果没有,方法则报错,这个方法未定义,属性则返回undefined。
function Person(name){
this.name = name;
}
Person.prototype = {age:24};
var tsrot = new Person("tsrot");
console.log(tsrot.name); //tsrot
console.log(tsrot.age); //24
console.log(tsrot.toString()); //[object Object]
当你用构造函数(构造函数我们一般首字母大写)建立一个对象时,它的原型链就是:tsrot ==》 Person.prototype ==》 Object.prototype ==》 null
如果没有定义Person.prototype这一环,则直接跳到下一环。
prototype与__proto__
在Javascript中,每个函数都有一个原型属性prototype指向自身的原型,而由这个函数创建的对象也有一个__proto__属性指向这个原型,而函数的原型是一个对象(函数点prototype也是一个普通对象,Function.prototype除外,它是函数对象,但它很特殊,他没有prototype属性),所以这个对象也会有一个__proto__指向自己的原型,这样逐层深入直到Object对象的原型,这样就形成了原型链。普通对象没有prototype,但有__proto__属性。