原型相关知识

2022-12-15 0 852

程序语言

第一类是甚么?为何程序语言?

程序语言 —— 方法论北迁更为灵巧、标识符F83E43Se性高、度的模组化

第一类是对一般而言球体的单纯抽象化,是两个罐子,PCB了特性和方式

特性:第一类的状况

方式:第一类的犯罪行为
// 单纯对象 const Course = { name:华为 age: 18, startCourse: function (name) { return `我是${name}` }, } // 表达式第一类 function Course() { this.name = 华为 this.age = 18 this.startCourse = function (name) { return `我是${name}` } }

构造表达式

js 其实本质上并不是基于类,而是基于构造表达式 + 蓝本链;constructor + prototype

构造表达式

function Course(name) { this.name = name this.age = 18 this.startCourse = function (name) { return `我是${name}` } } const course = new Course(小白)

Course 本质是构造表达式

1. 表达式体内使用的 this,指向所要生成的实例

2. 生成第一类用 new 来进行实例化

3. 可以做初始化传参

问题:

如果构造表达式不初始化,可以使用吗? – 无法使用

如果项目中需要使用,通常(不被外界感知)如何解决?
function Course() { // 判断this指向 const _isClass = this instanceof Course if (!_isClass) { // 如果this不是指向Course的话就实例化两个 return new Course() } this.name = 华为 this.age = 18 } // 使用方 const course = Course()

ps:如果编写底层的 api 标识符时,尽量做到不需要让外部感知内部类型

new表达式

new 是甚么 / new 的原理 / new 时候做了些甚么?

function Course() {} const course = new Course() 1. 创建了两个空第一类作为返回的第一类实例 2. 将生成空第一类的蓝本第一类指向了构造表达式的 prototype 特性 3. 将当前实例第一类赋给了内部 this 4. 执行构造表达式的初始化标识符 实例特性影响 独立互不影响

new 的单纯表达式实现

分析:

第一类的声明方式 let obj = new Object() // 可以发现obj也是两个第一类,这里称实例第一类 console.log(obj, obj) console.log(typeof Object) //function Object是两个表达式 console.dir(Object) console.log(obj.__proto__, objproto_) // new 小结: // 创建了两个全新的第一类。 // 这个第一类会被执行[[Prototype]](也是__proto__)链接。 function Course() { this.name = 华为 this.age = 18 console.log(this) //指向实例第一类course } // 使用方 const course = new Course() console.log(course, course) console.log(course.__proto__ === Course.prototype) //true // new 小结:// 1.生成的新第一类会绑定到表达式调用的this // 2.生成的每个实例第一类会链接到这个Course.protytype第一类上,蓝本第一类 function Course(name) { this.name = name // Null(空) null // Undefined(未定义) undefined // Number(数字) 1 // String(字符串)1 // Boolean(布尔) true // Symbol(符号)(第六版新增) symbol // Object(第一类) {} // Function(表达式) function(){} // Array(数组) [] // Date(日期) new Date() // RegExp(正则表达式)/a/ // Error (错误) new Error() return } var course = new Course1(whaha) console.log(course1) // new小结:前面六种基本类型return 都会正常返回{name: whaha}, // 后面的Object(包含Functoin, Array, Date, RegExg, Error)都会直接返回这些值。

表达式实现:

总结 1. 创建了两个全新的第一类 2. 这个第一类会被执行[[Prototype]]也是**proto**链接 3. 生成的新第一类会绑定到表达式调用的 this 4. 生成的每个实例第一类会链接到这个 Course.protytype 第一类上,蓝本第一类 5. 如果表达式没有返回第一类类型 Object(包含 Functoin, Array, Date, RegExg, Error) 那么 new 表达式中的表达式调用会自动返回这个新的第一类 function Mnew(fn) { if (typeof fn !== function) { throw Mnew function must be a function } let obj = new Object() // 把原表达式蓝本挂载到生成的新第一类上面 obj.__proto__ = fn.prototype let argsArr = […arguments].slice(1) //生成表达式返回值,如果表达式没有返回第一类类型Object,则是undefined,否则是自动返回该Object的值 let newObj = fn.apply(obj, argsArr) // 判断newObj是否是Object,null也会是Object类型 所以排除 let isObj = typeof newObj === object && newObj !== null // 当然Object类型(包含Functoin, Array, Date, RegExg, Error)这五种也需要单独判断,因为会直接返回该类型值 这里不单独判断 console.log(isObj, isObj) if (!isObj) { return obj } else { return newObj } } function Person(v, b) { this.name = xiao this.age = v this.h = b // return {} } let p = Mnew(Person, 18, 222) Person.prototype.getY = function () { console.log(哈哈哈) } p.getY() console.log(p.__proto__ === Person.prototype) //true

constructor特性

function Course(a, b) { this.a = a this.b = b } const course = new Course(华为, 小白) console.log(Course.prototype)

1. 每个第一类在创建时,会自动拥有两个构造表达式特性 constructor

2. constructor 继承自蓝本第一类,指向了构造表达式的引用

问题:使用构造表达式会有甚么性能上的问题?

function Course(name) { this.a = 1 this.b = 2 this.startCourse = function (name) { return `我是${name}` } } const course1 = new Course(es6) const course2 = new Course(OOP)

==> 构造表达式中的方式,会存在于每两个生成的实例里,重复的挂载其实是会导致资源浪费

解决办法:蓝本挂载。

蓝本第一类

function Course() {} const course1 = new Course() const course2 = new Course()

1. 构造表达式: 用来初始化创建第一类的函数 – Course

— 自动给构造表达式赋予两个特性 prototype,该特性等于实例第一类的蓝本第一类

2. 实例第一类:course1 是实例第一类,根据蓝本第一类创建出来的实例

— 每个第一类中都有两个–proto–

— 每个实例第一类都有两个 constructor

— constructor 有继承而来,并指向当前的构造表达式

3. 蓝本第一类:Course.prototype
function Course() {} Course.prototype.teacher = 华为 const course1 = new Course() const course2 = new Course() // 对上篇蓝本第一类做优化 function Course() { this.name = Yy this.age = 华为} // 方式挂载于prototype上 Course.prototype.startCourse = function (name) { return `我是${name}` } const course1 = new Course(es6) const course2 = new Course(OOP)

蓝本和蓝本链

蓝本

1. 任何两个表达式都有 prototype 特性

2. 表达式的 prototype 特性值是两个第一类,我们把这个第一类叫做蓝本(蓝本第一类),这也叫显式蓝本,它也是两个普通的第一类。

3. 蓝本的作用:通过构造表达式创建的实例第一类可以直接访问这个构造表达式的 prototype 特性上的任意成员

4. 所有的引用类型都有两个’\_ _ proto_ \_特性(也叫隐式蓝本,它是两个普通的第一类)

5. 所有引用类型,它的’\_ _ proto_ \_特性指向它的构造表达式的’prototype’特性。
// prototype function Person() {} console.dir(Person) //可以发现Person里面有个prototype特性console.dir(Person.prototype) //可以发现prototype是两个第一类 叫做蓝本(蓝本第一类) // 蓝本上可以挂载方式表达式和特性等 Person.prototype.name = 小明 Person.prototype.getFun = function () { console.log(我是, this.name) } let p = new Person() p.name // 小明 p.getFun() // 我是 小明 let p2 = new Person() p2.name // 小明 p2.getFun() // 我是 小明 总结 1. 当把特性方式添加到蓝本上去时候通过构造表达式创建的实例第一类可以直接访问蓝本上的任意成员 2. 解决了内存浪费问题, 在内存中只有一份方式,在蓝本中 3. 不用手动创建第一类,只要是表达式,就会自带蓝本第一类
// __proto__特性 function Person() {} let p = new Person() console.log(p.__proto__ === Person.prototype) // true

访问蓝本第一类,有两种写法

1. 通过构造表达式来访问: 构造表达式的 prototype 特性 prototype: 显式蓝本

2. 通过实例第一类来访问: 实例第一类的__ proto__ 特性 __ proto __ : 隐式蓝本

注意:__ proto__ 特性是私有特性,不是个标准的特性,IE 浏览器不兼容,不要通过__ proto__ 特性来访问和修改蓝本中的成员

蓝本链

当访问两个第一类的某个特性时,会先在这个第一类本身特性上查找,如果没有找到,则会去它的–proto–隐式蓝本上查找,即它的构造表达式的 prototype,如果还没有找到就会再在构造表达式的 prototype 的–proto–中查找,这样一层一层向上查找就会形成两个链式结构,我们称为蓝本链。

特性搜索原则: 沿着蓝本链往上查找

1. 查找第一类的特性,首先在第一类自身查找是否拥有该特性,如果有,就返回出去

2. 如果没有,去第一类的蓝本上去查找,如果有,就返回出去

3. 如果也没有,沿着第一类的蓝本链继续往上查找,直到 Object.prototype 蓝本第一类上,如果有,就返回出去

4. 如果还没有,返回 undefined
原型相关知识

Function 和 Object

1. 表达式是 Function 构造表达式的实例

2. 第一类是 Object 构造表达式的实例

// 表达式 const fn1 = new Function( name, age, console.log(`我是${name}, 我今年${age}岁`) ) fn1(小明, 10) console.log(Function.prototype === fn1.__proto__) // true // 第一类 const person2 = { name: 小明, age: 10 } const person3 = new Object() person3.name = 小明 person3.age = 10 console.log(Object.prototype === person2.__proto__) // true console.log(Object.prototype === person3.__proto__) // true

那 Function 构造表达式和 Object 构造表达式他们两个又是谁的实例呢?

new Object()和 new Function() 2 个其实都是构造表达式,所以本质都是 Function 的实例

– function Object()其实也是个表达式,所以他是 Function 构造表达式的实例

– function Function()其实也是个表达式,所以他也是 Function 构造表达式的实例,他是他自己本身的实例
console.log(Function.prototype === Object.__proto__) // true console.log(Function.prototype === Function.__proto__) // true
原型相关知识

constructor

是蓝本第一类中自带的属性

蓝本第一类中的 constructor 特性的值, 是当前的构造表达式
function Person() {} console.log(Person.prototype) console.dir(Person.prototype.constructor) var p = new Person() // p没有constructor特性,p访问的是蓝本上的constructor的特性, // 蓝本的constructor特性指向了 构造表达式Personconsole.log(p.constructor == Person) // true console.log(p.constructor == Person.prototype.constructor) // true console.log(Person.prototype) // {constructor: Person} console.log(Person.prototype.constructor === Person) // true “` “`js // 构造表达式和蓝本第一类之间的关系 : 配偶关系 // 母亲(Person) 通过 prototype 特性找到 父亲( Person.prototype)=> Person + prototype === Person.prototype // 父亲 通过 constructor 特性找到 母亲 => Person.prototype.constructor === Person // 构造表达式和实例第一类之间的关系 : 母子关系 // 母亲(Person)通过 new 创建了 孩子(p) => new Person() ==> p // 孩子不能直接访问到妈妈 // 蓝本第一类和实例第一类之间的关系: 父子关系 // 孩子(p) 通过 __proto__ 特性找到 爸爸( Person.prototype) => p.__proto__ === Person.prototype // 孩子可以间接的访问到妈妈
原型相关知识

蓝本链

Person.prototype 和 Function.prototype

– Person.prototype,它是构造表达式 Person 的蓝本第一类

– Function.prototype,他是构造表达式 Function 的蓝本第一类

蓝本第一类,可以知道其实这两个本质都是第一类,本质肯定都是通过 new Object()来创建的

Person.prototype 和 Function.prototype 都是构造表达式 Object 的实例
function Person() {} var p = new Person() console.log(Person.__proto__ === Function.prototype) // true // 构造表达式的 __proto__(隐式蓝本)是Function的蓝本,换句话说Person是Function 构造表达式的实例 console.log(Person.prototype.__proto__ === Object.prototype) // true console.log(Function.prototype.__proto__ === Object.prototype) // true __proto__的路径就叫蓝本链 function Person() {} var p = new Person() // p的蓝本链 console.log(p.__proto__ === Person.prototype) //true console.log(Person.prototype.__proto__ === Object.prototype) //trueconsole.log(Object.prototype.__proto__) // null // Person的蓝本链 console.log(Person.__proto__ === Function.prototype) console.log(Function.prototype.__proto__ === Object.prototype) // 实例第一类p的蓝本链: // p ==> Person.prototype ==> Object.prototype ==> null;// Function 构造表达式的实例Person的蓝本链: // Person=> Function.prototype=>Object.prototype=>null const person = new Object() console.log(Object.prototype === person.__proto__) //true// 第一类蓝本链: // person=>Object.prototype=>null
原型相关知识

关于 Object.prototype

任何第一类的蓝本链都有 Object.prototype 蓝本第一类意味着任何对象都可以访问到 Object.prototype 蓝本上任何成员 hasOwnProperty 方式是 Object.prototype 上的方式 作用判断特性是否是第一类自身的特性如果是第一类自身的就返回 true 语法第一类.hasOwnProperty(“特性”) function Person(name) { this.name = name } Person.prototype.gender = male var p = new Person() console.log(p.hasOwnProperty(name)) // true console.log(p.hasOwnProperty(gender)) // false console.log(p.hasOwnProperty(sex)) // false

in 运算符

语法:特性 in 第一类

作用: 判断特性是否在第一类的蓝本链上,如果在,就返回 true,说白了,判断第一类是否能够使用该特性
function Person(name) { this.name = name } Person.prototype.gender = male Object.prototype.legs = 2 var p = new Person() console.log(name in p) // true console.log(gender in p) // true console.log(sex in p) // false console.log(hasOwnProperty in p) // true console.log(p.hasOwnProperty(hasOwnProperty)) // false

hasOwnProperty: 判断特性是否是第一类自身的

for (var k in p) { // 需求:只需要遍历得到p第一类自身的特性即可 // 可以使用 hasOwnProperty 方式 来过滤出第一类自身的特性 if (p.hasOwnProperty(k)) { // 成立了,k变量所代表的特性是p第一类自身的 console.log(k) } // for…in 在遍历的时候,会把第一类蓝本链上可以遍历的特性都能遍历到 // console.log(k); // name gender legs

小结:

in : 判断特性是否在蓝本链上,

hasOwnProperty: 判断特性是否第一类自身的

hasOwnProperty 使用场景: 在 for…in 遍历第一类的时候,过滤出第一类自身的特性

继承

在蓝本第一类的所有特性和方式,都能被实例所共享

蓝本继承

// Game类 function Game() { this.name = lol } Game.prototype.getName = function () { return this.name } // LOL类 function LOL() {} LOL.prototype = new Game() LOL.prototype.constructor = LOL const game = new LOL() // 本质:重写蓝本第一类方式,将父第一类的特性方式,作为子第一类蓝本第一类的特性和方式

蓝本链继承有甚么缺点:

function Game() { this.name = lol this.skin = [s] } Game.prototype.getName = function () { return this.name } // LOL类 function LOL() {} LOL.prototype = new Game() LOL.prototype.constructor = LOL const game1 = new LOL() const game2 = new LOL() game1.skin.push(ss)

– 1. 多个实例对引用类型的操作会被篡改,此时特性属于子类的共享特性了

– 2. 实例化子类时,无法向父类做传参

构造表达式继承(经典继承)(解决蓝本继承造成的问题)

function Game(arg) { this.name = lol this.skin = [s] } Game.prototype.getName = function () { return this.name } // LOL类 function LOL(arg) { Game.call(this, arg) } const game3 = new LOL(arg) // 解决了共享特性的问题 + 子向父传参问题

蓝本链上的共享方式无法被读取继承,如何解决?

==>解决方案:组合继承
function Game(arg) { this.name = lol this.skin = [s] } Game.prototype.getName = function () { return this.name } // LOL类 function LOL(arg) { Game.call(this, arg) } LOL.prototype = new Game() LOL.prototype.constructor = LOL const game3 = new LOL()

组合继承就没有缺点么?

功能上解决了所有的需求和缺陷。

问题在于:无论何种场景,都会调用两次父类构造表达式。 (调用 2 次 new 初始化)

– 1. 初始化子类蓝本时

– 2. 子类调用表达式内部 call 父类的时候

优化:解决方案: 寄生组合继承

function Game(arg) { this.name = lol this.skin = [s] } Game.prototype.getName = function () { return this.name } // LOL类 function LOL(arg) { Game.call(this, arg) } LOL.prototype = Object.create(Game.prototype) LOL.prototype.constructor = LOL

提高: 看起来完美解决了继承,但是如何实现多重继承?

function Game(arg) { this.name = lol this.skin = [s] } Game.prototype.getName = function () { return this.name } function Store() { this.shop = steam } Store.prototype.getPlatform = function () { return this.shop } function LOL(arg) { Game.call(this, arg) Store.call(this, arg) } LOL.prototype = Object.create(Game.prototype) // LOL.prototype = Object.create(Store.prototype); Object.assign(LOL.prototype, Store.prototype) LOL.prototype.constructor = LOL // LOL继承两类 const game3 = new LOL()

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务