原型和继承
__proto__
属性
对象有一个隐藏属性 [[Prototype]]
,指向其原型(父类型),如果没有原型则为 null
。
从对象中读取一个不存在的属性时,会自动往原型中查找这个属性,这就是“原型继承”行为。
访问/修改 [[Prototype]]
属性的方式为
__proto__
属性。__proto__
属性是[[Prototype]]
属性的getter/setter
,这是历史遗留问题,已经不推荐使用。- 推荐使用
Object.getPrototypeOf()
、Object.setPrototypeOf()
方法。(ES5 新增)
属性的迭代
使用
for in
循环迭代对象属性时会包含继承自原型的属性,如果不想考虑这些属性,可以通过obj.hasOwnProperty(prop)
判断条件来过滤,如果prop
是从原型继承来的,会返回false
。
prototype
属性
JavaScript 对象分为 函数对象 和 普通对象,每个对象都有 __ptoto__
属性,但 只有函数对象才有 prototype
属性。
构造函数、实例、原型三者的关系
(构造)函数 的 prototype
属性指向了一个对象,这个对象就是那些被该函数构造出来的所有 实例对象 的 原型(或者说父对象,__proto__
所指对象),每个对象都能从原型对象继承属性。
实例的
__proto__
指向构造函数的ptototype
对象。
每个原型都有一个 constructor
属性,指向关联的 构造函数。
function Person() {} // 构造函数
let person = new Person(); // 实例对象
person.__proto__ === Person.prototype // true
Person.prototype.constructor === Person // true
原型链
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
那么,原型的原型又是什么呢?
原型也是一个 对象,是通过 Object()
构造函数 生成的,那么原型就是这个构造函数的实例,那么它的原型就指向 Object.prototype
对象。
Object.prototype
对象是顶层对象,它的原型被设计为 null
。
参数按值传递
JavaScript 中所有函数的参数 都是按值传递的:
- 传递 基本类型值 给形参时,传递的就是 值本身;
- 传递 引用类型值 给形参时,传递的是一个 指针。(也叫按共享传递)
参数传递的本质就是一个 隐式赋值:
-
传递 基本类型变量
num1
给形参num
时,做赋值运算:num = num1
,形参保存的就是实参的值。 -
传递 引用类型变量
obj1
给形参obj
时,做赋值运算:obj = obj1
,因为变量obj1
保存的是一个对象的 地址,所以这个赋值运算就让形参obj
也指向了那个对象(那块内存)。- 如果后续又通过另一个 赋值运算 让
obj1
指向了另一个对象(rebinding),那么这和obj
是无关的。 - 而对
obj1
或者obj
的 修改 都是对它们所指向对象的修改(mutation),因为它们所指的是同一个对象。
注意 rebinding 和 mutation 的区别,rebinding 之后,原来的
obj1
变量就转而保存另一个对象的地址了,和原来对象就没有联系了。 - 如果后续又通过另一个 赋值运算 让
采用静态作用域
作用域 规定了 在哪里查找变量。JavaScript 采用 词法作用域,也就是 静态作用域。
静态作用域 vs 动态作用域
静态作用域:函数的作用域在函数定义时就已经确定了。
动态作用域:函数的作用域在 函数调用时才确定。
标签:执行,函数,对象,JavaScript,作用域,理解,深入,上下文,变量 From: https://www.cnblogs.com/lzh1995/p/16757596.html