目标
- 掌握
JavaScript
原型的相关知识点 - 了解
JavaScript
中作用域的特点
知识要点
- 原型、原型对象、实例之间的关系
- 原型链的定义
- 静态作用域(词法作用域) 和 动态作用域
1、原型
1.1、 关系
- 什么是原型?
原型(prototype)是函数的一个特殊属性,即指向原型对象的指针。
构造函数的原型就是prototype
属性,实例对象的原型就是__proto__
属性 - 什么是原型对象?
原型对象(prototype object)是一个属于其所在函数的空对象,可以添加属性和方法。其自身constructor属性指向其函数。 - 什么是实例对象?
实例对象就是构造函数通过new
构造出来的新实例
拿以下代码来进行说明:
function Person() {}
const person = new Person()
Person就是构造函数,person就是实例对象
它们之间的 关系 就不多加赘述了,直接如图所示
console.log(person.__proto__ === Person.prototype) //true
console.log(Person.prototype.constructor === Person) //true
console.log(Object.getPrototypeOf(person) === Person.prototype) //true
console.log(person.constructor === Person) //true
// 解析:person没有constructor属性,要从原型上找这个属性
// 相当于 person.__proto__.constructor === Person.prototype.constructor === Person
1.2、原型链
读取一个对象的属性时,如果该对象没有这个属性时,就会从该对象的原型上查找该属性,如果还查不到,就会从原型的原型上查找,一直找到最顶层为止。
以代码为例:
function Person() {}
Person.prototype.name = 'relex'
const person = new Person()
console.log(person) // 输出{}
console.log(person.name) // 输出name
实例对象的__proto__
指向实例原型,那么实例原型的__proto__
指向什么呢?
实例原型的__proto__
指向的当然是实例原型的原型了,它们之间的关系如下所示:
其实原型对象就是通过 Object
构造函数生成的,所以Object
就是Person
的原型,也就是Object
是实例对象person
的原型的原型。
而Object
的原型是null
,这意味着Object.prototype
没有原型,所以查找属性的时候查到Object.prototype
就可以停止查找了。其中,蓝色部分就是原型链。
总结:当在实例对象中访问某一个属性时,首先会在该对象内部(自身属性)查找,如找不到,则向其__proto__
指向的原型中寻找,如仍找不到,则继续向原型中的__proto__
指向的上级原型中寻找,直到找到或值为null
为止。
原型链的作用:查找对象的属性或方法。
2、作用域
作用域分为以下两种:
- 静态作用域(词法作用域)
函数的作用域在函数定义时确定,JavaScript采用的是静态作用域 - 动态作用域
函数的作用域在函数调用时确定
var value = 1
function foo() {
console.log(value)
}
function bar() {
var value = 2
foo()
}
bar() // 输出1
因为在JavaScript中,采用的是静态作用域,所以函数的作用域在函数定义的时候就决定了。
在静态作用域中,上述代码在执行foo函数的时候,先从函数内部查找是否有局部变量value,如果没有就根据书写位置,查找上面一层的代码,也就是value 等于1,所以结果输出1;
在动态作用域中,上述代码在执行foo函数的时候,先从函数内部查找是否有局部变量value,如果没有,就从调用函数的作用域,也就是bar函数内部查找value变量,所以结果会打印2
作用域的面试真题:
// case 1
var scope = "global";
function checkscope(){
var scope = "local";
function f(){
return scope;
}
return f();
}
checkscope(); // 输出local
// case 2
var scope = "global";
function checkscope(){
var scope = "local";
function f(){
return scope;
}
return f;
}
checkscope()(); // 输出local
这两段代码输出的都是local,因为f
函数都是在checkscope
函数中定义的,所以作用域就是在checkscope
函数中,而scope
在该函数中有被定义,所以就都返回该值
这两段代码主要区别是在执行上下文有所不同,详细内容见执行上下文的章节