程序语言
第一类是甚么?为何程序语言?
程序语言 —— 方法论北迁更为灵巧、标识符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()