- 一直搞不懂真的头大
一.先去看下什么是构造函数
constructor是原型prototype的一个属性,当函数被定义时候,js引擎会为函数添加原型prototype,并且这个prototype中constructor属性指向函数引用
ps:
因此重写prototype会丢失原来的constructor。 下面会说到这个点,
为了规范开发,在重写对象原型时一般都需要重新给 constructor 赋值,以保证对象实例的类型不被篡改。
二.什么是原型 什么是原型链
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- Object.getPrototypeOf() 方法返回指定对象的原型 ( 即, 内部[[Prototype]]属性)。 -->
<script>
let arr = ['abc']
console.log(arr.concat('123'))//0: "abc" 1: "123"
let hh = {}
let gg = {}
// __proto__ 理解为 父亲
console.log(Object.getPrototypeOf(hh) == Object.getPrototypeOf(gg))// true
let bb = { name :"宝宝" };
console.log(bb); //是有proto父亲的
console.log(bb.hasOwnProperty("name"));//true
// hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
// Object.create(proto,[propertiesObject])
//proto 新创建对象的原型对象。 创建对象的原型,表示要继承的对象
// propertiesObject(可选 ) 也是一个对象,用于对新创建的对象进行初始化
//没有原型的对象 叫做完全数据字典对象
let cc = Object.create(null, {
name: {
value: "lalala"
}
})
console.log(cc)//没有__proto__的
console.log(cc.hasOwnProperty("name"))//cc.hasOwnProperty is not a function
</script>
</body>
</html>
复制代码
proto 是一个对象 是找长辈里面属性的连接点 长辈里面内置有很多的属性 可以继承使用
在原型上面添加方法
let smailDog = {
bigDog() {
console.log('多毛大胖狗')
},
xiaoDog(){
console.log('拉布拉多小狗')
}
}
console.log(smailDog)
// bigDog: ƒ bigDog() xiaoDog: ƒ xiaoDog()
smailDog.__proto__.xiaoDog = function (){
console.log("小明")
}
smailDog.xiaoDog(); //输出的是 拉布拉多小狗
//自己有 就不用找父级的了
复制代码
函数有多个长辈
构造函数有prototype 属性
实例是没有prototype属性的
构造函数 有两个长辈 一个是prototype 另一个是proto 但是他们的使用场景是不一样的
User 跟实例 newUser 都是指向同一原型 所以实例是可以使用show 方法
newUser 实例去找原型 只能通过__proto__ 因为它没有prototype
ps:
这两个长辈是不一样的 他们两个长辈记住也是对象
prototype与proto服务场景:(他们两个都是原型 )
- prototype 一般是服务于 实例化对象的
- 而 proto 一般是服务于 构造函数本身的
Object 构造函数
打印:
console.dir(Object)
let xiaoMing = new Object();
Object.prototype.show = function() {
console.log('峡谷再无小明了')
}
xiaoMing.show();//峡谷再无小明了
复制代码
构造函数的实例化对象 可以用到 Object.prototype 里面的方法
在User的父级prototype.–proto–里面找到了show方法
在User的父级–proto–.–proto–里面找到了show方法
说明
console.log(User.prototype.proto == User.proto.proto)//true
可以把__proto__ 理解为查找父级的 连接点
看下Object.prototype
里面是没有父级的了
console.dir(Object.prototype.proto)//null
null是顶端原型了
<script>
console.dir(Object)
let xiaoMing = new Object();
Object.prototype.show = function() {
console.log('峡谷再无小明了')
}
xiaoMing.show();//峡谷再无小明了
function User() {}
console.dir(User)
console.log(User.prototype.__proto__ == User.__proto__.__proto__)//true
console.dir(Object.prototype)
console.dir(Object.prototype.__proto__)//null
let mingQi = new Object();
mingQi.show();
User.show();
console.dir(Object.__proto__)
console.dir(Object.__proto__.__proto__)//null
</script>
复制代码
构造函数的原型体现
let arr = []//new Array
console.log(Array.prototype)
console.log(arr.__proto__ == Array.prototype)
let obj = {}//new Object
console.log(obj.__proto__ == Object.prototype)
let bool = true //new Boolean
console.log(bool.__proto__ == Boolean.prototype)
let str = ""//new String
console.log(str.__proto__ == String.prototype)
String.prototype.show = function() {
console.log('调用了show方法哦')
}
str.show();//调用了show方法哦
</script>
复制代码
自定义原型设置
// Object.setPrototypeOf(),为现有对象设置原型,返回一个新对象
// 接收两个参数:第一个是现有对象,第二是原型对象。
let xm = { name :'小明'};
let xmParent = {
name:'小明爸爸',
show(){
console.log('parent method:' + this.name)
}
}
console.dir(xm)
Object.setPrototypeOf(xm,xmParent)// parent method:小明
xm.show();
xmParent.show();//parent method:小明爸爸
console.log(Object.getPrototypeOf(xm))// {name: "小明爸爸", show: ƒ}
//Object.getPrototypeOf() 方法返回指定对象的原型 ( 即, 内部[[Prototype]]属性)。
</script>
复制代码
原型中constructor的引用
function User(name) {
this.name = name;
}
// User.prototype.show =function(){
// console.log('show:'+ this.name)
// }
// let lisi = new User.prototype.constructor('李四')// 相当于new Object('李四')
// console.log(lisi)// User {name: "李四"}
// lisi.show();//show:李四
//如果是替换原型 添加多个方法:
User.prototype = {
constructor : User,
show() {
console.log(this.name)
},
view() {
console.log('User.prototype中的 view method')
}
}
let lisi = new User.prototype.constructor('李四')// 相当于new Object('李四')
console.log(lisi);
lisi.show();//lisi.show is not a function
//为了解决这问题 在上面{}中加上 constructor : User, 输出:李四
</script>
复制代码
通过原型找到构造函数 构造函数实例化新对象
自定义方法 创建新对象 new User(‘’) 被自定义函数替换掉
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// function User(name) {
// this.name = name;
// }
// let lisi = new User.prototype.constructor('李四')// 相当于new Object('李四')
// console.log(lisi)//{name: "李四"}
// function User(name) {
// this.name = name,
// this.show = function () {
// console.log(this.name);
// }
// }
// let hd = new User('拉不拉多')
// console.log(hd)//{name: "拉不拉多" show: ƒ ()}
// // 现在要创建一个新对象 xj = {name: "牛逼" show: ƒ () }
// function createByObject(obj, ...args) {
// //拿到传进来的那个变量的 原型 比如User.prototype 再去找他的构造函数
// const constructor = Object.getPrototypeOf(obj).constructor
// return new constructor(...args)//new User.prototype.constructor('李四')// 相当于new Object('李四')
// }
// let mq = createByObject(hd, '溟七');
// console.log(mq.name)//溟七
// mq.show();//溟七
// 现在要改变原型了 把show方法放到原型上
function User(name) {
this.name = name
}
User.prototype = {
constructor: User,
show(){
console.log(this.name);
}
}
let hd = new User('拉不拉多')
console.log(hd)//{name: "拉不拉多" show: ƒ ()}
function createByObject(obj, ...args) {
//拿到传进来的那个变量的 原型 比如User.prototype 再去找他的构造函数
const constructor = Object.getPrototypeOf(obj).constructor
return new constructor(...args)//new User.prototype.constructor('李四')// 相当于new Object('李四')
}
let mq = createByObject(hd, '溟七');
console.log(mq.name)//溟七
mq.show();//溟七
</script>
</body>
</html>
复制代码
原型继承的体现形式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let a = {name:'a'}
let c = {name:'c'}
let b = {name:'b',
show(){
console.log(this.name)
},
view(){
console.log('view method')
}
}
Object.setPrototypeOf(a,b)
Object.setPrototypeOf(c,b)
console.log(a)
a.show();
c.show();
a.view();
c.view();
</script>
</body>
</html>
复制代码
Object.isPrototypeOf
- 明确判断对象是否是其他对象原型链上的一份子
<script>
// JS isPrototypeOf()方法:检测一个对象是否存在于另一个对象的原型链中
// 或者说一个对象是否被包含在另一个对象的原型链中
let a = {}
let b = {}
let c = {}
// console.log(a.isPrototypeOf(b) ) //false a对象是否是b 对象原型链上的一份子
// console.log(Object.prototype.isPrototypeOf(a))//true Object.prototype 是a 的顶层
Object.setPrototypeOf(b,c)
Object.setPrototypeOf(a,b)
console.log(b.isPrototypeOf(a))
console.log(c.isPrototypeOf(a))
console.log(c.isPrototypeOf(b))
</script>
复制代码
in 与 hasOwnProperty 属性检测差异
in 会攀升原型链去查找
而hasOwnProperty只会去当前对象查找
let one = {
name: '换喜临门'
}
let tow = {
url: 'www.abc.com'
}
Object.prototype.web = "i love you"
// "name" 是指key
// console.log("name" in one)//true
// console.log("url" in one)//false
Object.setPrototypeOf(one, tow)
console.log("url" in one)//true
console.log("web" in one)//true
console.log("web" in tow)//true
console.log(one.hasOwnProperty('name'))//true 属性是否在自己身上
console.log(one.hasOwnProperty('url'))//false
console.log(one.hasOwnProperty('web'))//false
// for(const key in one){
// console.log(key)//name url web
// }
for (const key in one) {
// console.log(key)// 继承name url web
if (one.hasOwnProperty(key))
console.log(key) //不继承name
}
复制代码
sort()方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- sort() 方法用于对数组的元素进行排序,并返回数组 -->
<script>
//1.数字排序
// let arr = [2, 1, 3, 5, 4];
// arr = arr.sort((n1, n2) => {
// // return -1; //返回负值 交换顺序
// // return 0 或者 1 //返回正值 保持顺序不变
// console.log(n1, n2);
// return n2 - n1;
// // n2 - n1 从大到小
// // n1 - n2 从小到大
// });
// console.log(arr);//[5, 4, 3, 2, 1]
// ps:不传参数,将不会按照数值大小排序,按照字符编码的顺序进行排序;
// var arr = ['General','Tom','Bob','John','Army'];
// var resArr = arr.sort();
// console.log(resArr);//输出 ["Army", "Bob", "General", "John", "Tom"]
// var arr2 = [30,10,111,35,1899,50,45];
// var resArr2 = arr2.sort();
// console.log(resArr2);//输出 [10, 111, 1899, 30, 35, 45, 50]
// sort默认的排序方式为字母排序,
// 2.字母排序(sort默认排序)
var arr = ["za", "zb", "a", "b", "xc", "xa"];
arr.sort();
console.log(arr);
// 运行结果:["a", "b", "xa", "xc", "za", "zb"]
// 3.对象属性排序 面试很常见的 根据数组中的对象的某个属性值排序
var obj = [
{ name: "lucy", num: 400 },
{ name: "nancy", num: 110 },
{ name: "maria", num: 200 },
{ name: "sb" },
{ name: "zhu" },
{ name: "dog", num: 'abc' }
];
obj.sort(compare("num"));
console.log(obj);
// 0: {name: "nancy", num: 110}
// 1: {name: "maria", num: 200}
// 2: {name: "lucy", num: 400}
// 3: {name: "sb"}
// 4: { name: "zhu" }
// 5: { name: "dog", num: "abc" }
// compare()中参数必须是这个对象的属性名称,而你要比较的这些对象里面,应该要有这个属性名称,否则会出错
//数组对象属性值排序
function compare(property) {
return function (a, b) {
//value1 - value2升序
//value2 - value1降序
var value1 = a[property];
var value2 = b[property];
return value1 - value2;//升序 小到大
}
}
var arr5 = [{ id: 10 }, { id: 5 }, { id: 6 }, { id: 9 }, { id: 2 }, { id: 3 }];
arr5.sort(function (a, b) {//
return a.id - b.id
})
console.log(arr5);
//输出新的排序
// {id: 2}
// {id: 3}
// {id: 5}
// {id: 6}
// {id: 9}
// {id: 10}
// 4.根据数组中的对象的多个属性值排序,多条件排序;
var arr6 = [{ id: 10, age: 2 }, { id: 5, age: 4 }, { id: 6, age: 10 }, { id: 9, age: 6 }, { id: 2, age: 8 }, { id: 10, age: 9 }];
arr6.sort(function (a, b) {
if (a.id === b.id) {//如果id相同,按照age的降序
return b.age - a.age
} else {
return a.id - b.id
}
})
console.log(arr6);
//输出新的排序
// {id: 2, age: 8}
// {id: 5, age: 4}
// {id: 6, age: 10}
// {id: 9, age: 6}
// {id: 10, age: 9}
// {id: 10, age: 2}
// 面试题:
let arrDiy = [3, 15, 8, 29, 102, 22]
console.log(arrDiy.sort())
// 0: 102
// 1: 15
// 2: 22
// 3: 29
// 4: 3
// 5: 8
// 按照unicode编码进行排序的 会先进行toString()方法
// ["3", "15", "8", "29", "102", "22"]
// 第一次排序结果: ["15","102","29","22","3","8"]
// 第2次排序结果: ["102","15","22","29","3","8"]
// 数字 => 大写字母 => 小写字母 => “中文”
var newArr = [3, 15, 8, 29, 102, 22]
newArr.sort(function (a, b) {
// console.log('排序:' + a +"," + b) //两个两个进行对比
console.log(a + "-" + b + "=" + (a - b)) //看表达式 像第一个减第二个 但是结果不是
// 15 - 3 =12 3 15
// 8 - 15=-7 8 15
// 29 - 8=21 8 29
// 102 - 29=73 29 102
// 22 - 102=-80 22 102
//规律 后面的数值和前面的数字相减
return a - b
})
console.log(newArr)
// [3, 8, 15, 22, 29, 102]
//底层算法:
</script>
</body>
</html>
复制代码
借用方法
打个比方说: 你家没车 你爸爸妈妈也没买车 你爷爷奶奶也没车 你邻居是我 我有奔驰 你可以借用我的车去开
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//你家没车 你的邻居有车 你要借用人家的车
var Car = function () {
this.name = "车";
}
var benz = new Car();
console.log(benz.name);//车
console.log(benz)
//在原型上面加对象
Object.setPrototypeOf(benz, { name: "小轿车", price: "23456" }); //__proto__ :{name:"小轿车",price:"23456"}
console.log(benz.name); //车
console.log(benz.price);//23456
//在原型上面加方法
let hh = {
data: [12, 50, 60, 42, 10, 5]
}
Object.setPrototypeOf(hh, {//在原型上添加方法
max() {
return this.data.sort((x, y) => y - x)[0];
}
})
console.log(hh.max())//60
let xj = {
lessons:{js:87,php:100,node:99,linux:45},
// get date(){//why 什么要加get?
// get date(){//why 什么要加get? 方法要同名
get data(){//why 什么要加get?
return Object.values(this.lessons)
}
}
console.log(hh.max.apply(xj))//xj中没有data属性 就读取不到 那么就在xj对象中加上一个方法返回 成绩的分数
</script>
</body>
</html>
复制代码
call 与apply 传递的参数 可用扩展运算符转换
let mq = {
data:[1,2,336,445,52,12,120]
}
console.log(Math.max(mq.data))//NaN
console.log(Math.max.apply(null,mq.data))//445
//如果用call 就要展开
console.log(Math.max.call(null,...mq.data))//445
let xm = {
lessons:{
math:100,english:0,huaxue:20,shengwu:95
}
}
console.log(Math.max.apply(null,Object.values(xm.lessons)))//100
console.log(Math.max.call(null,...Object.values(xm.lessons)))//100
复制代码
改变构造函数原型不是继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Menber(){
}
function Admin(){
}
function User(){
}
User.prototype = {
constructor:User,
name:function(){
console.log('user.name');
}
}
let a = new User();
a.name()
Admin.prototype = User.prototype;
Admin.prototype.role = function() {
console.log('admin.role');
}
Menber.prototype = User.prototype;
Menber.prototype.role = function() {
console.log('Menber.role');
}
let b = new Admin();
b.role();//Menber.role 会被覆盖
let c = new Menber();
c.role();//Menber.role
</script>
</body>
</html>
复制代码
继承是原型的继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Menber(){
}
function Admin(){
}
function User(){
}
User.prototype = {
constructor:User,
name:function(){
console.log('user.name');
}
}
let a = new User();
a.name()
// Admin.prototype.__proto__= User.prototype;
// Admin.prototype.role = function() {
// console.log('admin.role');
// }
//放在定义之前b.role is not a function
Admin.prototype = Object.create(User.prototype)
Admin.prototype.role = function() {
console.log('admin.role');
}
Menber.prototype.__proto__= User.prototype;
Menber.prototype.role = function() {
console.log('Menber.role');
}
let b = new Admin();
b.role();//admin.role
let c = new Menber();
c.role();//Menber.role
</script>
</body>
</html>
复制代码
多继承的问题
Admin 实例想用credit 和request 原型里面的方法
member实例想用credit 和request 原型加上 member里面的方法
但是 Admin是不需要用到member原型上的方法的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 封装好的方法
function inherit(child, father) {
child.prototype = Object.create(father.prototype)
// 设置constructor 不可以遍历
Object.defineProperty(child.prototype, 'constructor', {
value: child,
enumerable: false
})
}
function Credit() { }
inherit(Credit, Address)
Credit.prototype.total = function () {
console.log('统计积分');
}
function Request() { }
//注意:继承要写在原型之前
inherit(Request, Credit)
Request.prototype.ajax = function () {
console.log("请求后台");
}
function Address() {
}
//ps :这里是要实现 Credit 继承Address 所以在Credit.prototype 原型改变之前就要实现继承 所以是加到上面的位置 而不是这里
// inherit(Credit, Address)
Address.prototype.getAddress = function () {
console.log('获取地址');
}
//上面的都要写在user之前
function User(name, age) {
this.name = name;
this.age = age;
}
inherit(User, Request);
User.prototype.show = function () {
console.log('你好呀,陆明清');
}
function Admin(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Admin, User)
// function Address(){
// }
// inherit(Credit,Address)
// Address.prototype.getAddress = function(){
// console.log('获取地址');
// }
let newAdmin = new Admin("京动", 123);
console.log(newAdmin);// {name: "京动", age: 123}
newAdmin.ajax();//请求后台
newAdmin.total();//统计积分
//getAddress是Admin实例不用的
newAdmin.getAddress();//获取地址
function Member(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Member, User)
let newMember = new Member('啦啦', 12);
console.log(newMember);
newMember.getAddress();
newMember.ajax();
newMember.total();
</script>
</body>
</html>
复制代码
使用mixin (混合)实现多继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 封装好的方法
function inherit(child, father) {
child.prototype = Object.create(father.prototype)
// 设置constructor 不可以遍历
Object.defineProperty(child.prototype, 'constructor', {
value: child,
enumerable: false
})
}
const Credit = {
total() {
console.log('统计积分');
}
}
const Request = {
ajax () {
console.log("请求后台");
}
}
const Address = {
getAddress(){
console.log('获取地址');
}
}
//上面的都要写在user之前
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log('你好呀,陆明清');
}
function Admin(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Admin, User)
//把对象压到原型上
Admin.prototype = Object.assign(Admin.prototype, Credit, Request)
// console.log(Admin.prototype);//{total: ƒ, ajax: ƒ, constructor: ƒ}
let newAdmin = new Admin("京动", 123);
console.log(newAdmin);// {name: "京动", age: 123}
newAdmin.ajax();//请求后台
newAdmin.total();//统计积分
// newAdmin.getAddress();//用不了的
function Member(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Member, User)
Member.prototype = Object.assign(Member.prototype,Credit,Request,Address)
let newMember = new Member('啦啦', 12);
console.log(newMember);
newMember.getAddress();
newMember.ajax();
newMember.total();
newMember.show();//你好呀,陆明清
</script>
</body>
</html>
复制代码
mixin的内部继承与super关键字
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 封装好的方法
function inherit(child, father) {
child.prototype = Object.create(father.prototype)
// 设置constructor 不可以遍历
Object.defineProperty(child.prototype, 'constructor', {
value: child,
enumerable: false
})
}
const Request = {
ajax () {
return "请求后台";
}
}
// Credit 默认原型是指向 Object.prototype
const Credit = {
__proto__:Request,
//需求就是Credit 里面也要用到ajax()
total() {
// console.log(this);//Admin {name: "京动", age: 123} 实例调用.totol() 其实它会先找到原型Admin.prototype 里面的total 实质上这个this是指 是Credit构造函数这个对象 它的原型上Request 才有ajax()
console.log(this.__proto__.ajax() + '统计积分');
// console.log(super.ajax() + '统计积分');
}
}
const Address = {
getAddress(){
console.log('获取地址');
}
}
//上面的都要写在user之前
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.show = function () {
console.log('你好呀,陆明清');
}
function Admin(name, age) {
//初始化实例数据
User.call(this, name, age);
}
inherit(Admin, User)
//把对象压到原型上
Admin.prototype = Object.assign(Admin.prototype, Credit, Request)
// console.log(Admin.prototype);//{total: ƒ, ajax: ƒ, constructor: ƒ}
let newAdmin = new Admin("京动", 123);
console.log(newAdmin.ajax());;//请求后台
newAdmin.total();//请求后台统计积分
</script>
</body>
</html>
复制代码