js进阶第一天
成长必经之路
目录
- 构造函数: 原型对象及原型链
- 构造函数: 继承
构造函数-原型对象
构造函数的实例成员
目标: 实例成员的内存问题
- 通过
构造函数封装后
,创建多个对象,对象调用同一个方法
,在内存中执行的并不是同一个方法
//1.构造函数设置形参
function Person(name, age) {
this.name = name;
this.age = age;
this.sing = function () {
console.log("我会唱歌");
}
}
//创建:创建是实例化对象!
let p1 = new Person("zs", 18); let p2 = new Person("ls", 28);
//问题
//1.实例化对象功能方法:打印结果一摸一样
console.log(p1.sing); //调用,我会唱歌
console.log(p2.sing); //我会唱歌
// 2.回顾,函数是什么数据类型?复杂
console.log(p1.sing == p2.sing); //false
//3.结论:两个方法是功能完全一样,来自各功能的实例化对象;但是内存是不同的地址;形成内存浪费!
- 如上问题会导致内存浪费, 传统方式也是这样的问题
原型对象prototype
目标: 知道什么是原型对象prototype及作用
prototype: 叫原型对象,
是每一个构造函数身上的一个属性,该属性是以对象的形式存在的(原型对象)
```javascript //导入: //构造函数:学习其他的知识是可以解决内存浪费的问题 //字面量的方式:到此为止;不能解决内存浪费的问题!
//知识: //prototype是属性名,构造函数上属性:prototype //值:对象;如果给自定义构造函数上添加方法,添加到prototype属性值对象上! //解决内存的问题! function Person(name, age) {this.name = name;this.age = age } //语法 Person.prototype.sing = function () { console.log(“我会唱歌”); } Person.prototype.eat = function () { console.log(“我会跳舞”); } //调用:实例化对象 let p1 = new Person(“zs”, 15) let p2 = new Person(“ww”, 19) //测试1:是否有sing方法 p1.sing(); p2.sing() //我会唱歌 //测试2:看两个方法是否功能是一样? console.log(p1.sing); //ƒ () {console.log(“我会唱歌”);} console.log(p2.sing); //ƒ () {console.log(“我会唱歌”);} console.log(p1.eat); //ƒ () {console.log(“我会跳舞”);} console.log(p2.eat); //ƒ () {console.log(“我会跳舞”);} //测试3:测试两个方法是否来自同一个内存空间 ?(内存地址) console.log(p1.sing == p2.sing); //true // 结论:学习prototype,给未来的实例化对象上设置一些方法,直接设置给 构造函数.prototype上, // Fn/Person: 构造函数 // let p1 = new Person(); 实例化对象; // prototype 原型对象;
- 作用: 通过原型对象设置构造函数中功能的方法, 设置公共方法, 解决内存浪费问题!
- 语法: `构造函数.prototype.方法名 = function(){ };`
<a name="50aff5c1"></a>
### 对象原型`__proto__`
> 目标: 能够了解**proto**的作用
1. 导入: 解释数组的实例化, 为什么都可以使用push方法? 而且是同一个功能, 同一个地址!
2. 为什么实例化 会有构造函数原型 prototype上的方法
1. 实例化对象上有一个属性名 **proto** , 属性值是一个对象; **proto** 上保存有对象的地址, 地址指向哪里? 指向构造函数的`prototype` ,
1. `ldh.__proto__==Star.prototype` 返回true; 保存了一个地址, 是同一个地址;
```javascript
function Person(name, age) {this.name = name;this.age = age;};
// 语法:
Person.prototype.sing = function () {console.log("我会唱歌");};
// 实例化
let p1 = new Person("zs", 15);
let p2 = new Person("ls", 26);
// ***问题1:p1 p2 为什么上有sing这个方法?
// 知识:
//实例化对象:对象就会有 __proto__:上面有sing方法 有属性:constructor
//注意:__前后都是两个下划线
// true:p1.__proto__ 和 原型对象 其实是一个东西;
//原型对象上有sing p1.__proto__上也有同样这个sing方法
console.log(p1.__proto__ == Person.prototype); //true
console.log(p2.__proto__ == Person.prototype); //true
// ***问题2:为什么这样调用呢 p1.sing(); 合理:p1.__proto__.sing();
// __proto__:原型链:
// 记住规则:如果 实例化对象.xxx();
// 1.先在自己实例化对象上先找下是否有xxx方法,如果没有;
// 2.会去实例化.__proto__去找;
// __proto__:非标准属性,开发过程中不出现代码;
// 为了给方法寻找提供链的方向;
console.log(p1); //Person {name: "zs", age: 15}
constructor构造函数
目标: 掌握constructor及作用
- 概念: constructor构造函数,每一个原型对象prototype和对象原型身上都有属性
- 作用: 通过constructor构造函数记录当前对象属于哪个构造函数的
//构造函数:创建对象,实例化对象
// 封装插件
// 创建对象比直接使用字面量形式创建对象更加性能好
function Person() {}
// 原型对象:添加占内存一个空间方法;
// 默认自带属性:constructor:构造函数;
// console.log(Person.prototype.constructor);
// 意义:通过原型对象, 知道隶属于哪个构造函数而已!
// 实例化.__proto__ 知道实例化对象由谁构造出来!
let p1 = new Person();
console.log(p1); //Person {}
- 构造函数,原型对象,对象原型之间的关系
原型链
目标: 原型链及作用
概念: 每一个对象都会proto,指向是哪里的地址呢?
- 原型链的形成:
- 每一个对象有一个proto属性,地址指向为其构造函数的原型对象prototype,及为同一个对象;
构造函数的原型对象prototype也是一个对象,也有proto属性,这样一层一层往上找就形成了原型链;
- 每一个对象有一个proto属性,地址指向为其构造函数的原型对象prototype,及为同一个对象;
__proto__的意义
:就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此
实际开发中,不可以使用这个属性,我们只需要知道它的指向和构造函数的prototype
指向同一个地址;- 为什么要设置原型链?给对象上的方法设置查找机制:
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性或方法。
- 如果没有就查找它的原型对象(也就是 proto指向的 prototype 原型对象)。
- 如果还没有就查找原型对象的原型(
Object的原型对象
)。 - 依此类推一直找到 Object 为止(null)。
- 如果还没有,就报错这个对象没有这个方法; Uncaught Typ
// 原型链
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sing = function () {
console.log("我会唱歌");
}
//实例化对象:
let p1 = new Person("zs", 18)
//原型对象:也是对象,发现这个对象上_proto_
// 对象其实也应该是某个构造函数的实例化对象
let obj1 = Person.prototype;
//Object:date内置构造函数
//{}字面量简单方式 object();内置构造函数的方式,创建空{}
// Object.prototype
console.log(Object.prototype == obj1._proto_); //true
//obj1 其实就是objet一个实例化
// 原型链,不是逻辑, 是客观存在; 是什么 ? 一个对象,有_proto_ 指向其原型对象: 原型对象其实也是个普通对象,
// 也有proto_属性,指向其原型对象Object.prototype;
原型链 -this
// this
let c;
function Person() {
this.name = "zs";
this.age = 18;
}
Person.prototype.sing = function () {
console.log("我会嫦娥"); //我会嫦娥
c = this
}
let a = new Person()
a.sing() //this赋值一份给c
console.log(c == a); //true
//-----------------使用------------------------
//落地:构造函数内部this,型对象上方法内this其实都是未来其次实例化对象!
function Person() {
this.name = "zs"
this.age = "28"
}
Person.prototype.sing = function () {
console.log(this.name + "会唱歌"); //true
}
- 练习:
1.查看原型对象上方法内部this的指向
2.给数组Array的原型对象上拓展求和的方法;
// 需求:让 你在 数组原型对象 上 添加一个求和的方法;getSum
// 1. 找到 数组 原型对象 Array.prototype
// console.log(arr.__proto__); // 非标准属性,开发不用!
// console.log(Array.prototype); // Array Object 内置构造函数名!
// 2. 往对象上填加新的方法
Array.prototype.getSum = function() {
// 3. 求和:数组求和封装为一个方法
// 封装步骤:
// 1.实际过程;
// 2.套个函数壳子;
// 3.设置形参(调用的时候是否需要传入实参)?返回值(调用的时候,看是否要得到函数执行结果)?
// this考虑:实例化对象;
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
// 遇到报错:
// 1.解读英文 "xxx" of null / undefined
// 2.告诉报错位置;
var arr = [10, 20, 30];
var res = arr.getSum();
console.log(res);
构造函数-继承
继承
目标: 掌握属性和方法继承
实例属性的继承: 直接复制?
//1------------------手动复制方式:维护不方便!
// other内部属性名发生改变,My内部不会自动改变!
function Other(house, money,car){
this.house = house;
this.money = money;
this.car = car;}
function My(house,money,car) {
this.house = house;
this.money = money;
this.car = car;}
实例属性的继承: call语法
// 2---------------------新的语法:一次性把对方身上所有属性全部拿过!
// call:打电话呼叫;基础语法
//
function other(house,money,car){
this.house = house;
this.money = money;
this.car = car;}
var obj = {
info:"我就是个单独对象",
};
other.call(obj,10,20,10);
// call语法规则:【需要重点记忆】
// 1.Other丞数肯定是要执行!
// 2.参数:第一个参数other执行,other内部this上的所有的属性名即将要去到这个参数上;
// 3.剩余参数,必须逗号分隔;就是oTher执行时,需要传入的实参;
console.log(obj);
实例属性的继承: call方式实现
//构造函数的内部数据的继承:
function other(house, money,car) {
this.house = house;
this.money = money;
this.car = car;}
function My2(a, b,){
//这个My2构造函数内部的实例化对象
other.call(this,a, b,c);
//相当于:
// this.house = a;
//this.money = b;
// this.car - c;}
继承
原型对象上的方法继承
//别人写好杓造函数+原型对象
function other(house){
this.house = house;
}
other.prototype.show = function() {
console.log("你看老子多有钱,我有”+this.house +“套房子");
};
Other.prototype.sing - function() {
console.log(“你看老子多有钱,我会唱歌“);
};
//自己:
function My (house){
other.call(this,house);
}
方式1: 把其他构造函数的原型对象直接使用( 不可取)
My.prototype = other.prototype;
My.prototype.dance = function() {
console.log("我会跳舞");
}
// m实例化:上面有show sing dance这些方法没有问题;
// var m = new My(10);
// m.show();
// m.sing();
//ll m.dance();
//执行结果:o这个实例化也有dance这个方法,other.prototype被修改了!
var o = new other(2e);
console.log(o);
方式2: 直接修改原型对象的proto属性值为 对方的原型对象( 逻辑正确, 代码不合适)
//默认情况:My.prototype._proto_ -= Object.prototype
//现在修改:相当于做了手术,做了修改,把other原型对象上方法继承过来;
My.prototype._proto_ = other.prototype;
// 1.测试My
My.prototype.dance = function() {
console.log("我是y原型对象上方法dance");
};
方式3: 最终方案
My.prototype = new other();
// My.prototype 本质是_proto__ : Object的原型对象﹔自带了constructor:My;1/
// 想被替换为_proto__本来就是指向other原型对象;
// let o = new other(); o._proto__本来就是指向other原型对象﹔
My.prototype.constructor = My;
//对上面的分解
//var o = new other(50); // o{house:50 ,__proto__:other.prototype}
// My.prototype = o;
My .prototype.dance = function() {
console.log("我是ly原型对象上一个方法");
}