面向对象-原型和原型链

本文详细介绍了JavaScript中的原型对象、原型链的概念,包括如何通过构造函数创建实例化对象,以及原型对象如何实现属性和方法的继承。同时,还涉及了垃圾回收机制的基本原理和实例化对象的__proto__属性。
摘要由CSDN通过智能技术生成

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一个实例话对象经历的四个阶段:

  1. 创建一个空的实例化对象
  2. 让构造函数中的this指向空的实例化对象
  3. 执行(调用)构造函数,从而创建实例化对象自身的属性和方法
  4. 返回实例化对象

原型对象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>)]

因为上面模式存储的不方便,我们才有了原型对象

总结:

  1. ** proto ** 属性:所有构造函数new出来的实例话对象,都有一个proto属性,该属性指向构造函数的原型对象
  2. 原型对象的意义:实例话对象在访问属性和方法时,会先访问自身的属性和方法,如果自身不存在对应的属性和方法,则会自动通过proto属性找到原型对象,如果原型对象上存在,则也能正确调用
  3. 原型对象是为了实现继承

原型链

原型对象 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. 给数组对象添加一个新的方法,实现去重
    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());
  1. 程序题
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();
  1. 程序题
// 函数声明优先: 如果普通变量和函数名冲突,最终变量中存储的是函数
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的实例

  1. prototype原型对象:是函数才有的属性
  2. 函数的__proto__都指向Function.prototype
  3. __proto__内部原型,是对象才有的属性
  4. 对象由函数(构造器)生成

实例对象的__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);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值