构造对象有两种方式
- 方式1: 构造函数+prototype 或者你就理解为一个函数+prototype
- 方式2: es6语法class
- class语法只是一种语法糖
原型链的理解
- [anyobject].--proto-- === [Object].prototype // 公式1 含义是:对象anyobject是由什么构造出来的?
- [anyfunction].prototype.constructor === [anyfunction] // 公式2 含义是:任何一个构造函数的prototype上的构造函数指针指向这个构造函数自身
- Object.prototype.--proto-- === null // 就是这么设计的
- Function.--proto-- === Function.prototype // 带入公式1
- Function.prototype.proto === Object.prototype // 带入公式1 然后思考Function.prototype这是一个对象,对象的构造函数当然是Object啦
- 所有函数一出生就就有prototype, 所有prototype一出生就有constructor属性,constructor属性一出生就保存着对应函数的地址
基础定义
- 原型:存放公有方法的地方,简化了代码 也就节省了内存
- 构造函数:可以new出对象的函数就叫构造函数**,开头是大写的!!**
- Array Function Date RegExp Object等都是class,也就是对象这种复杂数据类型分支下的分类名称而已
- **在对象这种复杂数据类型中,Object是顶级的分类,是最上面的 **
- 言语的表达上,可以说 数组是一种对象,也可以说数组是一类class ✔
- 七大数据类型是对 JS 中数据的分类,与class是两回事
- 数据类型中人们说的对象,和Object不是一回事
- 正确的口头表达是:'对象'/object/Object这三种写法都可以被认作是一种数据类型,名叫对象✔
- **大写的Object可以被称之为是一种构造函数 ✔ **
- **大写的Object是对象这种数据类型的一个class ✔ **
- 每个新的函数都出厂自带prototype,这是很特殊的,因为每个obj是不会自带prototype的
Object.prototype(直接或间接)是所有对象的原型
为什么Function instanceof Object为true?
Function.__proto__ === Function.prototype //Function是一个构造函数,也就是函数,那么一个函数是由什么构造出来的?当然Function类
Function.prototype.__proto__ === Object.prototype //Function.prototype是一个对象没错吧,那么对象是由什么构造出来的?当然就是Object类啦
// 就此,我们证明了Function.__proto__.__proto__ === Object.prototype
// 当然你也可以console.dir(Function) 连续两次打开它的隐藏属性__proto__去查看
首字母大写的Math不是构造函数,而只是一个普通的对象
关于原型在口语中的正确描述
- obj.--proto-- 与 Object.prototype 都只是地址,地址指向一个叫原型的空间,描述的是同一个东西
- 只要一个东西是由另外一个构造出来的那么 那么这个东西就是有原型的
- 句式1: 子对象的原型是构造函数.prototype 或者 子对象的原型是子对象.--proto--
- 例如我们可以说obj的原型是Object.prototype ✔
- 例如我们可以说obj的原型是obj.--proto--,与上面这句是一模一样的 ✔
- 例如我们可以说Object的原型是Function.prototype ✔
- 句式2: 构造函数.prototype是子对象的原型 或者 子对象.--proto--是子对象的原型
- 例如我们可以说Object.prototype是obj的原型 ✔
- 例如我们可以说Function.prototype是Object的原型 ✔
- 例如我们可以说Object.--proto-- 是 Object 的原型,与上面这句是一模一样的 ✔
- Object 的原型是 Object.--proto-- ✔而不是Object.prototype ❌
重要理解
构造函数开头是大写的
普通函数开头是小写的
构造函数和普通函数都有 prototype 属性,只不过普通函数的prototype属性没什么用
构造函数往下找用 Xxx.prototype
构造函数往上找用 Xxx.--proto--. 或者省略这个--proto--隐藏属性
obj.--proto-- 与 Object.prototype 都是地址,地址指向一个叫原型的空间
[anyfunction].prototype.constructor === anyfunction // true 这算是一个公式
// 例子1
Object.prototype.constructor === Object // true
// 任何一个函数都有prototype,因为公式,任何一个函数.prototype.constructor是函数自身
// 例子2
let fn1=function(){};
fn1.prototype.constructor === fn1 // true
// 例子3
为什么Function.--proto--.constructor === Function?
Function.__proto__ === Function.prototype // Function是一个函数,函数当然是由Function类型的对象构建的
Function.prototype.constructor === Function // 任何一个函数.prototype.constructor是自身
对象的构造函数是Object
let obj={};
obj.__proto__.constructor === Object // true 你就问自己 obj是通过谁创造的? Object
// 套用公式解题:
obj.__proto__ === Object.prototype // 公式1
Object.prototype.constructor === Object // 公式2
函数的构造函数是Function
let fn1=function(){};
fn1.__proto__.constructor === Function // true 你就问自己 fn1是通过谁创造的? Function
// 套用公式解题:
fn1.__proto__ === Function.prototype // 公式1
Function.prototype.constructor === Function // 公式2
window是Window构造函数构造出来的
window.__proto__ === Window.prototype; // true 通过原型判断
window.constructor === Window; // true 通过构造函数判断 其实是window.__proto__.constructor === Window;
Window是Function类构造出来的
因为Window本身就是构造函数,是一个函数,当然是由Function类构造出来的
Window.constructor === Function // true 这其实是Window.__proto__.constructor
Object是Function类构造出来的
因为Object本身就是构造函数,是一个函数,当然是由Function类构造出来的
window.Object.constructor === window.Function // true 这其实是window.Object.__proto__.constructor
Function也是Function类构造出来的
Function是构造函数,是一个函数,当然是由Function类构造出来的
window.Function.constructor === window.Function // true 这其实是window.Function.__proto__.constructor
为什么arr.--proto--.--proto-- 指向的是Object的公有方法?
var prototypeObj={name:'ryan'};
var obj=Object.create(prototypeObj); // 让prototypeObj这个对象成为obj的原型,即obj.__proto__就是prototypeObj的内容
obj.__proto__ === prototypeObj // true 这里得到了印证
prototypeObj.__proto__ === Object.prototype // 当然是成立的,因为prototypeObj这个普通对象本身就是由Object构造函数所创建的
var arr=[1,2,3];
arr.__proto__ === Array.prototype; // Array.prototype也是一个Obejct类型对象,只要是对象,必然继承Object的公有属性
Array.prototype.__proto__ === Object.prototype;
检测一下是否理解了原型链?
// 运行以下代码,创建构造函数,让你回答以下问题
let square=[];
let width=5;
function Square(width){
this.width=width; // 为new出来的新对象提供私有属性
}
Square.prototype.getArea=function(){ // 为new出来的新对象提供公有属性
return this.width * this.width;
}
Square.prototype.getLength=function(){
return 4 * this.width;
}
square=new Square(width);
// 可以用 === 依次验证一遍
square.__proto__ 指向什么? Square.prototype
square.__proto__.__proto__ 指向什么? Object.prototype
Square.__proto__ 指向什么? Function.prototype
Square.__proto__.constructor 指向什么? Function
Square.prototype.__proto__ 指向什么? Object.prototype
// 因为prototype是一个对象,那么一个对象的原型自然是来自于Object.prototype
Square.prototype.constructor 指向什么? Square
命名规范
- 普通函数小写开头且动词开头 createElement
- 构造函数大写开头且名词 new Object()
如何实现继承
已知4个正方形的边长,输出他们的面积与周长
- V1-V3都是一样的东西,不用多虑
V1 用到了原型 但是未封装构造函数
let squareList=[];
let widthList=[5,6,5,6];
let squarePrototype={ // 原型对象 ,注意这只是一个对象,甚至不是函数,当然就不叫构造函数
getArea(){
return this.width * this.width;
},
getLength(){
return 4 * this.width;
}
}
for(let i=0;i<4;i++){
squareList[i]=Object.create(squarePrototype); // 为每个数组元素创建对象,并设置原型
squareList[i].width=widthList[i];
}
console.log(squareList); // 可以看到每个数组元素是有width属性的,而且原型是squarePrototype
squareList[0].getArea(); // 可以得到面积
squareList[0].getLength(); // 可以得到周长
V2 用到了原型且封装了构造函数
但是上面这个代码太松散了,不够有组织结构,那么我们现在制作一个有组织的构造函数
// 现在制作一个有组织的构造函数createSquare
let squareList=[];
let widthList=[5,6,5,6];
function createSquare(width){ // 构造函数
let obj = Object.create(squarePrototype); // 把原型对象灌进obj
obj.width = width;
return obj
}
let squarePrototype={ // 原型对象
getArea(){
return this.width * this.width;
},
getLength(){
return 4 * this.width;
}
}
for(let i=0;i<4;i++){
squareList[i]=createSquare(widthList[i]);
}
console.log(squareList);
squareList[0].getArea();
squareList[0].getLength();
V3 将原型与封装好的构造函数一体化
但是上面的代码中函数和原型依旧是分散的,能否让他们更加紧密呢?
**答案是可以的,因为函数也是object类型,所以也有.prototype **
// 原型与构造函数互相引用
let squareList=[];
let widthList=[5,6,5,6];
function createSquare(width){ // 构造函数
let obj = Object.create(createSquare.squarePrototype);
obj.width = width;
return obj
}
createSquare.squarePrototype={ // 构造函数身上的原型对象,足够紧密了吧
getArea(){
return this.width * this.width;
},
getLength(){
return 4 * this.width;
},
constructor:createSquare // 指向构造函数,这是必须要这么做的,虽然没什么用,参照公式2
// [anyfunction].prototype.constructor === [anyfunction]
}
for(let i=0;i<4;i++){
squareList[i]=createSquare(widthList[i]);
}
console.log(squareList);
squareList[0].getArea();
squareList[0].getLength();
es6前的终极版本 new + this 这是JS之父为我们提供的便捷
- 与上面不同的是,不用Object.create了,直接挂在构造函数.prototype
- 构造函数名记得要首字母大写 Square
// new一个对象
let squareList=[];
let widthList=[5,6,5,6];
function Square(width){
this.width=width; // 为new出来的新对象提供私有属性
}
Square.prototype.getArea=function(){ // 为new出来的新对象提供公有属性
return this.width * this.width;
}
Square.prototype.getLength=function(){
return 4 * this.width;
}
for(let i=0;i<4;i++){
squareList[i]=new Square(widthList[i]);
}
console.log(squareList);
squareList[0].getArea();
squareList[0].getLength();
new的4个作用
let obj = new Constructor()
- 自动创建空对象
- 为空对象关联原型,即设置obj.--proto-- = Constructor.prototype
- 让this指向空对象并且运行构造函数
- 自动return 新创建的对象
以下面这个例子为例解释一下即可
function Animal(login) {
this.login = login;
this.sayHi = function() {
console.log(this.login); //undefined
}
}
var dog = new Animal('John');
console.log(dog)
请画出let x = {}的内存图
Object.prototype.--proto-- ==== null
数组Array只是一种对象
let arr=[1,2,3];
Object.keys(arr);
// ["0", "1", "2"] 由此可见数组Array只是一种对象
如何理解数组的公有方法 push pop shift unshift
搞清楚英文名的意思即可
用class语法新建一个class
class Square{
constructor(width){
this.width=width; // 私有属性
}
getArea(){ // 写在原型上的方法
return this.width * this.width;
}
getLength(){ // 第一种形式,几乎只用这种写法
return 4 * this.width;
}
}
以下两种写法引以为戒即可
getLength=()=>{ // 第二种形式,几乎不用这种写法
return 4 * this.width;
}
getLength:function(){ // 错误的形式
return 4 * this.width;
}
typeof与instanceof的区别
注意 typeof 可以判断数据类型,唯独null比较特别,
typeof null === 'object'typeof function(){} === 'function'
- 这两个在判断函数的参数时会有用到
- typeof主要是用于判断一个东西对应的是JS 7种数据类型中的哪一种
- 当你要判断的这个参数是object类型时 比如 arr fn obj等等
- 就请你 arr instanceof Array 这样的方式去判断
- fn instance of Function
- obj intance of Object
typeof返回值为字符串
- 特例是typeof null // 为"object"而不为null
- 在所有typeof结果中能返回"object"的实在太多了,因此这个typeof局限性很大,判断不精准是哪一种对象
instanceof用以比较真假
- 本质是console.dir(anyObj)以查看其结构,然后你一层一层拨开--proto--以观察原型链
- 比如上面详解过的Function instanceof Object为true是因为Function.__proto.proto === Object.prototype
typeof 小例子
let hashmap=[{name:'ryan',no:1},{name:'leo',no:2}]
let string=JSON.stringify(hashmap);
console.log(typeof string); // string
console.log(typeof hashmap); // object