开始前的碎碎念
计算机这门学科算是一门往前往后都看不到尽头的学科,我们能做的选择其中的一部分进行专精与深入,正所谓实力不足所以要提前开始准备,这样才能避开这个行业底层饱和中坚不足的现状,在最后拿到自己想要的offer,一起fighting!!
正文内容
原型、原型链
//举一个例子!!!!!!!!!!!!!!!!!!
function Animal (name) {
this.name = name;
}
Animal.prototype.color = 'white';
Animal.prototype.weight = "1000kg";
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
console.log(cat1.name);
console.log(cat2.name);
console.log(cat1.color);
console.log(cat2.color);
console.log(cat1.weight);
console.log(cat2.weight);
输出结果:
通过结果我们显而易见的发现,除了名字在构造时使用了传入的参数,两个cat又多出来了两个共有的属性,关键点在于这个prototype
。prototype是一个只有函数具有的属性,它指向一个对象,这个对象就是调用该构造函数而创建的实例的原型。
那么原型就可以理解为每一个js对象在创建时(除了null),都会关联的一个对象(通俗理解为每生一个儿子都要告诉它爹是谁),每次创建的这个js实例都会“继承”下来这些共有的属性。
(补充:这里其实并不是真正的继承,继承意味着复制操作,但是 JavaScript 默认并不会复制对象的属性,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。)
我们通过查资料后发现了一个和prototype相对应的东西:__proto__
,这是一个只有js实例具有的属性,通过验证,我们发现这个属性指向对象的原型。
console.log(cat1.__proto__===Animal.prototype);
//控制台打印 true,说明这俩代表的是一个东西
那这个里面究竟包含了什么东西呢,我们打印一下看看
console.log(cat1.__proto__);
console.log(Animal.prototype);
我们惊奇的发现原型里还有一个 constructor
,并且发现这个属性指向所关联的构造函数
console.log(cat1.__proto__.constructor === Animal);
console.log(Animal.prototype.constructor === Animal);
//控制台打印了两个true!!!!!!
看到这里,我们捋一下:
prototype是函数具有的属性,指向原型
__proto__是js实例具有的属性,指向原型
constructor是原型所具有的属性,指向所关联的构造函数
于是我们可以大胆推测,在代码执行时,首先寻找实例的属性,找不到就会去实例的原型里找属性,找不到就一直找原型的原型。
function Animal(name) {
}
Animal.prototype.color = 'white';//设置原型属性
var cat1 = new Animal();
cat1.color = "black";//设置实例属性
console.log(cat1.color); //black
delete cat1.color;
console.log(cat1.color); //white
显而易见,的确按照我们推测的那样,先找实例,再找原型,那如果原型也没有,那原型的原型又是什么,通过分析我们发现原型也是个对象,那一个对象的基类就是Object类咯,那我们再来试验一下。
function Animal(color) {
this.color = "white"
}
var cat1 = new Object();
cat1.color = "black";
console.log(cat1.color)
console.log(Animal.prototype.color)//找原型
通过实验我们发现了当实例的原型也没有该属性的时候,就会一路往上找,顺着思路,那最顶层原型的原型是什么呢,试验一下
console.log(Object.prototype.__proto__);//顶层原型的原型 结果为null
结果也在意料之内,最顶端的地方就是万物的发源地,没有原型。看到这里,原型链也就可以理解为这一个个原型组成的链状结构。
词法作用域,动态作用域
作用域:就相当于规定一个变量有效的区域(比如你的公司万能门禁只能在公司范围内使用)
JavaScript 是采用词法作用域的,也就是静态作用域。
词法作用域:函数的作用域在函数定义的时候就决定了。
动态作用域:函数的作用域是在函数调用的时候才决定的。
var val = 1;
function A() {
console.log(val);
}
function B() {
var val = 2;
A();
}
B();
比如这段代码:
采用静态作用域(js中的执行结果):首先执行A(),在函数内部没找到变量val,就去上一层寻找,找到了,于是输出1。
采用动态作用域:首先执行A(),在函数内部没找到变量val,然后去A()的作用域里寻找val,就是B()里,找到了val是2,所以输出2。
总结
prototype是函数具有的属性,指向原型
__proto__是js实例具有的属性,指向原型
constructor是原型所具有的属性,指向所关联的构造函数
在真正执行时查找顺序:实例属性--->原型属性--->原型的原型属性--->...--->顶层原型属性
原型就可以理解为每一个js对象在创建时(除了null),都会关联的一个对象
原型链可以理解为一个个原型组成的链状结构。
词法作用域:函数的作用域在函数定义的时候就决定了,js就是采用这种
动态作用域:函数的作用域是在函数调用的时候才决定的。