12.面向对象-原型和原型链
什么是原型对象
function Student(name,adderss){
// 构造属性
this.name = name;
this.age = age;
this.address = address;
// 构造方法
this.say = function(){
console.log('我是',this.name,'我来自'+this.address);
}
}
var one = new Student('Jack',19,'郑州');
var two = new Student('Rose',15,'郑州');
总结:new一个实例话对象经历的四个阶段:
- 创建一个空的实例化对象
- 让构造函数中的this指向空的实例化对象
- 执行(调用)构造函数,从而创建实例化对象自身的属性和方法
- 返回实例化对象
原型对象prototype[理解]
function Person(name,age){
this.name=name;
this.age=age;
this.say=function(){
alert('我偷偷告诉你:'+this.name+"今年"+this.age+'岁了');
}
}
var p1 = new Person('jack',20); // 作为构造函数调用
p1.say();
var p2 = new Person('Rose',26); // 作为构造函数调用
p1.say();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hCP1ykVF-1692350743008)(<https://cdn.nlark.com/yuque/0/2022/png/22278242/1653883720960-53a99dc2-4a81-4c9a-b5d7-f715ebb43c97.png#clientId=ud64e7bdd-961c-4&errorMessage=unknown error&from=paste&height=799&id=uc28dbe17&name=image.png&originHeight=799&originWidth=929&originalType=binary&ratio=1&rotation=0&showTitle=false&size=391155&status=error&style=none&taskId=u1e7b950b-a9ca-40ff-8c3b-745e28582e5&title=&width=929>)]
因为上面模式存储的不方便,我们才有了原型对象
总结:
- ** proto ** 属性:所有构造函数new出来的实例话对象,都有一个proto属性,该属性指向构造函数的原型对象
- 原型对象的意义:实例话对象在访问属性和方法时,会先访问自身的属性和方法,如果自身不存在对应的属性和方法,则会自动通过proto属性找到原型对象,如果原型对象上存在,则也能正确调用
- 原型对象是为了实现继承
原型链
原型对象 prototype
原型对象也是对象,所以它也有原型,当我们使用或访问一个对象的属性或方法时:
- 它会先在对象自身中寻找,如果有则直接使用;
- 如果没有则会去原型对象中寻找,如果找到则直接使用;
- 如果没有则去原型的原型中寻找,直到找到Object对象的原型。
- Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回 null
原型对象默认不是空的,默认情况下里面有两个方法,分别是constructor[构造器]和** proto **
// 原型对象在初始化时,有两个属性:
// 1. constructor: 构造器,指向构造函数本身 (开发中基本不用,主要是理解)
console.log(Student.prototype.constructor);
// 2. __proto__:指向它上一级构造函数的原型对象
console.log(Student.prototype.__proto__);
也就是说,以后想要通过原型对象找到构造函数本身的化,可以使用constructor,但是基本上不用
案例:
function Student(name,age,address){
this.name = name;
this.age = age;
this.address = address;
this.say = function(){
console.log('我是',this.name,'我来自'+this.address);
}
}
Object.prototype.move = function(){ // 给Object添加原型方法
console.log('会移动');
}
var one = new Student('Jack',19,'郑州');
one.move(); // ok!从one中调用move方法
var d = new Date();
d.move() // 可以调用么?可以!
var arr = [1,2,3];
arr.charAt(); // 报错
arr.move(); // 可以!
总结:
1. 原型对象:所有的构造函数在初始化时,都会自动生成一个特殊的实例话对象,构造函数的prototype属性指向该对象,该对象称为原型对象,或prototype对象
2. __proto__对象:所有构造函数new出来的实例话对象,都有一个__proto__属性,该属性指向构造函数的原型对象,原型对象也有自己的__proto__属性,指向上一级构造函数的原型对象,__proto__会一直找到Object的原型对象,object原型对象的__proto__指向null
3. 原型链:由一系列__proto__属性,串联起来的原型对象,称为原型链
4. 原型链的意义:实例化对象在访问属性和方法时,会先访问自身的属性和方法,如果自身不存在对应的属性和方法,则会自动通过__proto__属性在整个原型链上查找,只要找到对应的属性和方法,就能正确执行,如果原型链上没找到,则报错
**注意:构造函数本身是Function对象new出来的实例话对象,所以构造函数Studnet也有 proto ,指向Function的原型对象(Function.protytype);
而Function.protytype的 proto **指向Object.prototype
**但是:**student这个构造函数new出来的实例话对象的原型链上并没有Function.prototype
面试题:什么是原型对象?什么是原型链?
原型对象:所有的构造函数在初始化的时候,会自动生成一个特殊的实例化对象,构造函数的prototype属性会执行该对象,该对象就被称为原型对象,或者prototype对象
什么是原型链:由一些列__proto__对象,串联起来的原型对象,称为原型链
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iDXo7rot-1692350743009)(<https://cdn.nlark.com/yuque/0/2022/png/22278242/1656315190248-3bbf29dd-d99f-41bc-9277-b203ebdc2acf.png#clientId=u15819cb0-fe7b-4&errorMessage=unknown error&from=paste&height=277&id=uf248f110&name=image.png&originHeight=498&originWidth=1062&originalType=binary&ratio=1&rotation=0&showTitle=false&size=177595&status=error&style=none&taskId=uf38760cb-c317-4f37-a880-c3b7bee39b9&title=&width=590.0000156296628>)]
原型链案例
- 给数组对象添加一个新的方法,实现去重
1)构建一个新的数组存放结果
2)for循环中每次从原数组中取出一个元素,用这个元素循环与结果数组对比
3)若结果数组中没有该元素,则存到结果数组中
Array.prototype.unique1 = function () {
var res = [this[0]]; // 结果数组
for (var i = 1; i < this.length; i++) {
var repeat = false;
for (var j = 0; j < res.length; j++) {
if (this[i] == res[j]) {
repeat = true;
break;
}
}
if (!repeat) {
res.push(this[i]);
}
}
return res;
}
var arr = [1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0];
alert(arr.unique1());
- 程序题
for(var i = 0; i < 10; i++){
//回调函数: 做为参数传入的函数称为回调函数
setTimeout(function(){ //异步/延时操作
console.log(i,'inner');
},0);
}
console.log(i,'outer');
3.程序题
var x = 1234;
function test(){
var x = 4567;
console.log(this.x);
}
test();
var testObj = new test();
var o = {};
o.x = 5678;
o.m = test;
o.m();
- 程序题
// 函数声明优先: 如果普通变量和函数名冲突,最终变量中存储的是函数
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.a=0; //静态变量
Foo.getName = function () { // 静态方法
//在构造函数上添加一个属性(因为函数也是对象)
console.log(2);
};
Foo.prototype.getName = function () { console.log(3); };
var getName = function () { console.log(4); };
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo().getName();
解析:静态属性不能被实例对象调用,即通过以new 操作符实例化后的对象调用,如上例中的a调用,只能通过类名调用,即Foo调用.也就是挂载在函数自身上的方法
本质上静态方法是定义在类上的,可以通过类名来访问,在静态方法中 this 是指向类本身,类实际上就是一个函数,一个构造器。
5.程序分析题
// 自定义对象的实例化对象,原型链上没有Function.prototype
// 而所有的函数实例化对象,_proto_属性指向Function.prototype, 而Function.prototype的_proto_属性指向Object.prototype
var F = function(){};
Object.prototype.a = function(){ console.log("hello")};
Function.prototype.b = function(){console.log("word")};
var f = new F();
var f1 = f.a;
f1();
var f2 = f.b;
// f2();
var f3 = F.a;
f3();
var f4 = F.b;
f4();
6.程序分析题
function A(){};
function B(a){ this.a = a};
function C(a){ if(a){this.a = a;}}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log( new A().a);
console.log( new B().a);
console.log( new C(2).a);
7.程序分析题
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.say = function(){
console.log(this.name1);
}
Cat.name1 = "kmf";
var cat1 = new Cat('cat1','white');
cat1.say();
Cat.say();
// 1. 执行new的时候this指什么
// 2.cat1.say()输出什么?
// 3.Cat.say() 输出什么?
案例:使用构造函数+原型实现气泡效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hth2CycN-1692350743009)(<https://cdn.nlark.com/yuque/0/2022/png/22278242/1653883795974-ba15f288-ad64-40a9-9307-7cde7e8f6a9a.png#clientId=ud64e7bdd-961c-4&errorMessage=unknown error&from=paste&height=407&id=u5b9e21b9&name=image.png&originHeight=407&originWidth=682&originalType=binary&ratio=1&rotation=0&showTitle=false&size=23990&status=error&style=none&taskId=ufc73ce3e-8827-407a-b69c-0af9c95c4f8&title=&width=682>)]
JS的垃圾回收(GC)机制
程序运行过程中会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢。所以我们需要一个垃圾回收的机制,来处理程序运行过程中产生垃圾。
当一个对象没有任何的变量或属性对它进行引用时,此时我们将永远无法操作该对象,此时这种对象就是一个垃圾,这种对象过多会占用大量的内存空间,导致程序运行变慢,所以这种垃圾必须进行清理。
上面这句话,也可以这样理解:如果堆内存中的对象,没有任何变量指向它时,这个堆内存里的对象就会成为垃圾。
JS拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁。我们不需要也不能进行垃圾回收的操作。我们仅仅需要做的是:如果你不再使用该对象,那么,将改对象的引用设置为 null 即可。
一道题理解原型和原型链
Object.prototype是一个对象,用于表示Object的原型对象
几乎所有的JS对象都是Object的实例
- prototype原型对象:是函数才有的属性
- 函数的__proto__都指向Function.prototype
- __proto__内部原型,是对象才有的属性
- 对象由函数(构造器)生成
实例对象的__proto__属性
指向构造函数的prototype属性
即:__proto__表示的是,实例与(构造函数的原型)之间的关系
var fn = function(){
this.f = "fn"
}
Object.prototype.a = function(){
console.log('aaa')
}
Function.prototype.b = function(){
console.log('bbb')
}
var f = new fn();
f.a() // 有没有a()
fn.a() // 有没有a()
fn.b() // 有没有a()
// console.log(f); 实例对象
// console.log(f.__proto__);
// console.log(f.__proto__.__proto__);
// console.log(Object.prototype);
// console.log(Object.prototype == f.__proto__.__proto__);
// console.log(f.__proto__.__proto__.a);