函数分类
- 普通函数;
- 类 (所有的类: 内置类、自己创建的类);
对象分类
普通对象;
数组;
正则;
日期;
Math;
实例 (除了基本类型的字面量创建的值);
prototype 的值也是对象类型;
原型
每一个类 (函数) 都具备 prototype 并且属性值是一个对象;
这个原型对象上天生具备一个属性 constructor 指向类本身;
每一个对象 (普通对象、prototype、实例、函数等) 都有一个 __proto__ 属性,这个属性指向当前实例所属类的原型 (prototype),如果不确定是谁的实例,一定都是 Object 的实例;
原型链
它是一种基于 __proto__ 向上查找的机制;
当操作某个属性或者方法的时候,首先在自己的私有空间中查找属性或者方法,其次沿着原型链查找:
- 若在私有空间找到了,则结束查找,使用自己的私有的即可;
- 若在私有空间没有找到,则基于 __proto__ 找所属的 prototype ,如果找到就使用这个公有的;如果没有找到,则基于 __proto__ 继续向上查找,一直找到 Object.prototype ,如果没有,则操作的属性或者方法不存在;
基础方法
W3C 不推荐直接使用系统成员 __proto__
API | 描述 |
---|---|
Object.getPrototypeOf(对象) | 获取对象的隐式原型 __proto__ |
Object.prototype.isPrototypeOf(对象) | 判断当前对象 (this) 是否在指定对象的原型链上 |
instanceof | 判断函数的原型是否在对象的原型链上 |
Object.create(对象) | 创建一个新对象,其隐式原型 __proto__ 指向指定的对象 |
Object.prototype.hasOwnProperty(属性名) | 判断一个对象自身是否拥有某个属性 |
面试题
实现 n.plus(10).minus(5)
~ function anonymous(proto) {
const checkNum = function checkNum(num) {
// 判断 num 是否是有效数字类型
num = Number(num);
// isNaN 是否是 非有效数字
if (isNaN(num)) {
num = 0;
}
return num;
};
proto.plus = function plus(num) {
// this:要操作的那个数字实例(对象)
// 返回 Number 类的实例,实现链式写法
return this + checkNum(num);
};
proto.minus = function minus(num) {
return this - checkNum(num);
};
}(Number.prototype);
let n = 10;
let m = n.plus(10).minus(5);
console.log(m); //=>15(10+10-5)
画图计算下面的结果
function fun() {
this.a = 0;
this.b = function () {
alert(this.a);
}
}
fun.prototype = {
b: function () {
this.a = 20;
alert(this.a);
},
c: function () {
this.a = 30;
alert(this.a);
}
}
var my_fun = new fun();
my_fun.b();
my_fun.c();
写出下面代码执行输出的结果
function C1(name) {
// name:undefined 没有给实例设置私有的属性 name
if (name) {
this.name = name;
}
}
function C2(name) {
this.name = name;
// 给实例设置私有属性 name => this.name=undefined
}
function C3(name) {
this.name = name || 'join';
// 给实例设置私有属性 name =>this.name=undefined || 'join'
}
C1.prototype.name = 'Tom';
C2.prototype.name = 'Tom';
C3.prototype.name = 'Tom';
alert((new C1().name) + (new C2().name) + (new C3().name));
//=> (new C1().name) 找原型上的 'Tom'
//=> (new C2().name) 找私有属性 undefined
//=> (new C3().name) 找私有属性 'join'
写出下面代码执行输出的结果(阿里)
function Foo() {
//没有 this.XXX() 就不是私有方法
getName = function () {
console.log(1);
};
return this;
}
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();// 2
getName();// 4
Foo().getName();// 1
getName();// 1
// new无参列表 优先级为18
// new有参列表 优先级为19
// 成员访问 优先级为19
// 先Foo.getName(),在new
new Foo.getName();// 2
// 先new,在调用.getName()
// Foo中没有this.getName,所以根据原型链向上查找
new Foo().getName();// 3
// 先 new Foo(),再.getName(),再 new 实例.getName,new 原型上的getName
new new Foo().getName();// 3
下面代码输出结果是什么?为什么?
// 题目
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
// 解析
// 扩展:模拟 array.push 的实现
Array.prototype.push = function push(num) {
// this:arr
this.length = this.length || 0;
// 拿原有 length 作为新增项的索引
this[this.length] = num;
// length 的长度会自动跟着累加 1
this.length++
};
let arr = [10, 20]; //=>{ 0: 10, 1: 20, length: 2 }
arr.push(30);
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
};
obj.push(1); // obj[2]=1 obj.length=3
obj.push(2); // obj[3]=2 obj.length=4
console.log(obj); // {2: 1, 3: 2, length: 4 ...}
let obj = {
1: 10,
push: Array.prototype.push
};
// this.length 默认值为 0
obj.push(20); // obj[obj.length]=20 obj[0]=20
console.log(obj); // {0:20,1:10,length:1...}
第 2️⃣ 座大山:面向对象
上一篇