构造函数、原型、继承原来这么简单?来吧,深入浅出,醍醐灌顶

}

// 为Star的原型对象添加方法(相当于将方法添加到Star的父类上去)

Star.prototype.cry=function(){

console.log(‘我要cry,cry,cry,cry,cry’)

}

Star.prototype.dance=function(){

console.log(‘一步一步,似魔鬼的步伐’)

}

// 通过输出发现,原型对象上确实有了 cry 方法

console.log(Star.prototype)

// 那么作为原型对象的子类的 Star 构造函数自然就拥有了cry 方法

var s1 = new Star(‘杜甫’, ‘男’)

s1.cry()

s1.dance()

var s2 = new Star(‘蔡徐坤’, ‘女’)

s2.cry()

s2.dance()

还可以使用对象的方式为原型添加多个方法

我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。

解惑:

1)这个原型,就类似于其他语言中的基类。。。。

2)不仅使我们自己使用构造函数或类定义的对象,JS中的内置对象的方法,其实都定义在这个对象的原型对象上

var arr=[]

// 查看对象的原型对象使用 proto 属性

console.log(arr.proto)

// 查看构造函数或类的原型对象使用 prototype 属性

console.log(Array.prototype)

// 总结:对象的__proto__ 属性和 类或者构造函数的 protptype 属性指向的是同一个对象

1.5对象原型

构造函数的prototype 属性获取的是当前构造函数的原型对象

构造函数的实例的__proto__属性获取的是当前对象的对象原型

这两者是一个对象,也就是说构造函数的原型对象与此构造函数的实例的对象原型是一个对象

在这里插入图片描述

在这里插入图片描述

function Star(name, gender) {

this.name = name

this.gender = gender

this.sing = function () {

console.log(‘小呀嘛小二郎’)

}

}

// 为Star的原型对象添加方法(相当于将方法添加到Star的父类上去)

Star.prototype.cry = function () {

console.log(‘我要cry,cry,cry,cry,cry’)

}

Star.prototype.dance = function () {

console.log(‘一步一步,似魔鬼的步伐’)

}

// 通过输出发现,原型对象上确实有了 cry 方法

console.log(Star.prototype)

// 那么作为原型对象的子类的 Star 构造函数自然就拥有了cry 方法

var s1 = new Star(‘杜甫’, ‘男’)

// s1.cry()

// s1对象的 __proto__属性获取是的是s1对象的对象原型

console.log(s1.proto)

// 验证构造函数的原型对象与实力的对象原型是一个对象

console.log(Star.prototype===s1.proto)

1.6constructor构造函数

对象原型( proto)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性(因为两个其实是一个东西) ,constructor 我们称为构造函数,因为它指回构造函数本身。

下面通过代码理解

function Star(name, gender) {

this.name = name

this.gender = gender

this.sing = function () {

console.log(‘小呀嘛小二郎’)

}

}

var s1 = new Star(‘杜甫’, ‘男’)

// 输出构造函数的原型对象

console.log(Star.prototype)

// 输出对象的对象原型

console.log(s1.proto)

在这里插入图片描述

通过上面的代码,我们看到,constructor 属性的值确实是这个对象对应的构造函数

不仅可以通过这个属性,获取原型对象所属的构造函数,constructor属性 还可以让原型对象重新指向原来的构造函数

一般情况下,对象的方法都在构造函数的原型对象中设置。

如下面这样

// 为构造方法的原型对象中添加speak 方法

Person.prototype.speak=function(){

console.log(‘人类说话’)

}

但是如果加入多个方法,使用上面的方式就比较麻烦

function Star(name, gender) {

this.name = name

this.gender = gender

}

// 像原型添加sing方法

Star.prototype.sing = function () {

console.log(‘红星闪闪放光彩’);

}

// 向原型添加 dance 方法

Star.prototype.dance = function () {

console.lo(‘魔鬼的步伐’)

}

// 向原型添加fly

Star.prototype.fly = function () {

console.log(‘上了飞机就拖鞋’)

}

像上面这样,如果要添加多个方法,我们可以给原型对象采取对象形式赋值,如下面的代码

Star.prototype = {

sing: function () {

console.log(‘红星闪闪放光彩’);

},

dance: function () {

console.lo(‘魔鬼的步伐’)

},

fly: function () {

console.log(‘上了飞机就拖鞋’)

}

}

但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了

此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

在这里插入图片描述

结果

在这里插入图片描述

总结:如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数如:

完整代码

function Star(name, gender) {

this.name = name

this.gender = gender

}

Star.prototype = {

construcotr:Star,

sing: function () {

console.log(‘红星闪闪放光彩’);

},

dance: function () {

console.lo(‘魔鬼的步伐’)

},

fly: function () {

console.log(‘上了飞机就拖鞋’)

}

}

console.log(Star.prototype)

1.7原型链

​ 每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。

可以类比基类,基类就是Object

其实所有的自定义的或者系统内置的构造函数或者类的最顶级的对象原型都是 Object

在这里插入图片描述

1.8构造函数实例和原型对象三角关系

1.构造函数的prototype属性指向了构造函数原型对象,构造函数的原型对象的constructor属性指向了构造函数

function Star(name, age) {

this.name = name

this.age = age

}

// 获取输出构造方法的原型对象

console.log(Star.prototype)

// 获取并输出原型对象的构造函数

console.log(Star.prototype.constructor)

// 证明原型对象的constructor属性确实获取的是对应的构造函数

console.log(Star.prototype.constructor===Star)

2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象

// 实例对象由构造函数创建

var s1=new Star(‘肖战’,18)

// 实例对象的__proto__属性指向了对应构造函数的原型对象

console.log(s1.proto)

3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型就是构造函数的原型对象,此对象中有constructor属性也指向了构造函数

在这里插入图片描述

重要说明:上面所说的理论同样适用于ES6中

class Star {

constructor(name){

this.name=name

}

}

var s=new Star(‘yhb’)

console.log(Star.prototype)

console.log(s.proto)

1.9原型链和成员的查找机制

任何对象都有原型对象,也就是prototype属性,任何原型对象也是一个对象,该对象就有__proto__属性,这样一层一层往上找,就形成了一条链,我们称此为原型链;

当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。

如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。

如果还没有就查找原型对象的原型(Object的原型对象)。

依此类推一直找到 Object 为止(null)。

__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

1.10原型对象中this指向

构造函数中的this和原型对象的this,都指向我们new出来的实例对象

1、构造函数中的this指向的就是new出来的对象

function Star(name, age) {

this.name = name

this.age = age

console.log(this)

}

var s1=new Star(‘李白’,20)

var s2=new Star(‘杜甫’,17)

在这里插入图片描述

2、构造函数的原型对象上的this指向的也是new出来的对象

function Star(name, age) {

this.name = name

this.age = age

console.log(this)

}

Star.prototype.sing=function(){

console.log(this)

}

var s1=new Star(‘李白’,20)

var s1=new Star(‘杜甫’,17)

在这里插入图片描述

通过下面的代码也可以比较两个到底是不是都只想了 new 出来对象

function Star(name, age) {

this.name = name

this.age = age

}

var that = null

// 构造函数的原型对象中的this

Star.prototype.sing = function () {

that = this

}

var s1 = new Star(‘李白’, 20)

// 韩炳旭说:sing方法一定要调用,否则无法执行赋值操作

s1.sing()

console.log(that === s1)

1.11通过原型为数组扩展内置方法

查看数组的原型

console.log(Array.prototype);

为数组扩展和一个方法,可以计算数组中元素的和

var numbers = new Array(1, 2, 3, 4, 5)

// 传统方法求和

// var sum = 0

// for (var i = 0; i < numbers.length; i++) {

// sum += numbers[i]

// }

// 通过改变原型

Array.prototype.sum = function () {

var sum = 0

for (var i = 0; i < this.length; i++) {

sum += this[i]

}

return sum

}

console.log(numbers.sum())

// 再创建一个数组,使用[]方式创建的也是Array类的实例

var arr=[2,3,4,7]

console.log(arr.sum())

console.log(Array.prototype)

2.继承


在ES6之前,没有extends 关键字实现类的继承,需要通过构造函数+原型对象的方式模拟实现继承,这种方式叫做组合继承

2.1call()

  • call()可以调用函数

  • call()可以修改this的指向,使用call()的时候 参数一是修改后的this指向,参数2,参数3(普通参数)…使用逗号隔开连接

下面代码单纯的调用函数

function f1() {

console.log(this) //window

}

f1.call()

下面代码修改this的指向

function f1() {

/*

*1)默认情况下,this=window

*2)使用call修改后,this=p

*/

console.log(this)

}

f1.call() // 输出window

// 创建对象p

var p={}

// 将函数f1的this指向修改为对象p

f1.call§ // 输出p

上面call 方法的第一个参数就是this引用的新对象,后面的参数作为函数 f1 的普通参数

代码演示

2.2子构造函数继承父构造函数中的属性

下面利用上面讲到的知识实现构造函数的继承(所谓继承,就是让一个类拥有另一个类中的属性和方法,换言之,就是将A类中的属性和方法的宿主换成B的对象)

步骤如下

  1. 先定义一个父构造函数

  2. 再定义一个子构造函数

  3. 子构造函数继承父构造函数的属性(使用call方法)

在这里插入图片描述

通过调试证明上面的结论

上面的案例只演示了属性的继承,方法的继承是一样的

在这里插入图片描述

2.3借用原型对象继承方法

上面的方式,可以继承构造函数(为了简单,以后我们称其为类)中的方法,但前面讲过,实际开发中,我们更喜欢将方法注册到类的原型上

function Father(name, age) {

this.name = name

this.age = age

}

Father.prototype.speak = function () {

console.log(‘hello’)

}

function Son(gender) {

// 注释 ①

Father.call(this, ‘张三’, 20)

this.gender = gender

}

var f=new Father(‘yhb’,30)

console.log(f)

var s = new Son(‘女’)

console.log(s)

输出结果

在这里插入图片描述

所以,Son 的对象中就没有 speak 方法

其实原因很简单,在Father的原型对象上添加了speak方法,那么Father会继承这个方法,但当前 Son与Father以及Father的原型并没有任何关系,我们只不过通过 call 方法巧妙的使用了其 name和age 属性而已,所以Son中自然没有speak 方法

如何解决呢?

我们可以修改Son的原型为Father的原型,这样的话Son就拥有了speak 方法

在这里插入图片描述

输出结果

在这里插入图片描述

我们发现,Son中确实拥有了speak 方法

但是问题也随之出现,因为 Father 与 Son 此时的原型对象是一个,所以此时我们如果想 Son的原型对象上添加一个方法

Son.prototype.run=function(){

console.log(‘run’)

}

再分别输出两个对象

在这里插入图片描述

这就违反了继承的原则:只能儿子继承父亲的,但现在是父亲也能继承儿子的

解决方案:将下面的代码

Son.prototype=Father.prototype

替换成下面这个

Son.prototype=new Father()

也就是让Son的原型对象指向 Father 的对象实例,这个实例的 proto 属性指向Father的对象原型,所以其中具有speak方法,但这仅仅是一个实例对象,就想从Father类拷贝了一份,修改此对象的原型,不会影响ather类的对象,所以修改此实例,不会影响到Father 的原型对象

稍微有点小问题就是,此时 Son的构造函数指向的是Father的构造函数了

console.log(s.constructor)

在这里插入图片描述

使用下面的代码再将其构造函数指回Son的构造函数

Son.prototype.constructor=Son

在这里插入图片描述

完整代码

/* 利用call 实现构造函数的继承*/

function Father(name, age) {

this.name = name

this.age = age

}

Father.prototype.speak = function () {

console.log(‘hello’)

}

function Son(gender) {

// 注释 ①

Father.call(this, ‘张三’, 20)

this.gender = gender

}

Son.prototype=new Father()

// 修改Son的构造函数为Son

Son.prototype.constructor=Son

// 像Son的原型对象中添加一个方法

Son.prototype.run=function(){

console.log(‘run’)

}

//var f=new Father(‘yhb’,30)

// console.log(f)

var s = new Son(‘女’)

console.log(s)

console.log(s.constructor)

图解:

3.ES5新增方法


关于 ECMAScript 与 JS 的关系,以及 ES 各个版本,大家可以自行百度,小编在这里不做展开说明

ES6 是在ES5的基础上升级,所以先学习ES5新增的特性,后面再学习ES6新增的特性

3.1数组方法forEach遍历数组

此方法用来便利数组,参数为一个函数

以后再想便利数组就不用自己编写循环了

var numbers = [1, 3, 5, 7, 9]

numbers.forEach(function (item, index, ary) {

console.log(‘元素:’ + item);

console.log(‘索引:’ + index);

console.log(‘数组本身:’ + ary);

})

案例:数组求和

var sum=0

numbers.forEach(function(item){

sum+=item

})

console.log(sum)

3.2数组方法filter过滤数组

此方法有一个返回值,用于接收所有符合过滤条件的元素,返回值的类型为数组

var numbers = [1, 2, 3, 4, 5, 6]

/*下面代码从数组numbers中筛选出所有>4的元素,然后放到新数组result中

注意:一定要有return 关键字

*/

var result = numbers.filter(function (item, index) {

return item > 4

})

console.log(result);

输出结果

在这里插入图片描述

3.3数组方法some

some 查找数组中是否有满足条件的元素

var arr = [10, 30, 4];

var flag = arr.some(function(item,index,array) {

//参数一是:数组元素

//参数二是:数组元素的索引

//参数三是:当前的数组

return item < 3;

});

console.log(flag);//false返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环

**some()** 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。

如果用一个空数组进行测试,在任何情况下它返回的都是false

语法:

arr.some(callback(element[, index[, array]])[, thisArg])

1)返回值为布尔值

2)找到满足条件的元素,就终止循环,不会继续向后查找

可以利用这个函数查询数组中是否存在某个值

var numbers = [1, 2, 3, 4, 5, 6]

function checkAvailability(arr, val) {

var res = arr.some(function (element, index) {

return element == val

})

return res

}

console.log(checkAvailability(numbers, 1))

3.4找水果案例

首先将数据定义在数组中,然后便利数组中的数据渲染到表格中,然后可以根据价格或者名称查询想吃的水果

  1. 定义数组对象数据

var fruits = [

{

id: 001,

title: ‘苹果’,

price: 5

},

{

id: 001,

title: ‘苹果’,

price: 5

},

{

id: 002,

title: ‘梨’,

price: 3

},

{

id: 003,

title: ‘香蕉’,

price: 6

},

{

id: 004,

title: ‘砂糖橘’,

price: 2

}

]

  1. 使用forEach遍历数据并渲染到页面中

var tbody = document.querySelector(‘tbody’)

fruits.forEach(function (item) {

var tr = `

${item.id} ${item.title} ${item.price} `

tbody.insertAdjacentHTML(‘beforeend’,tr)

})

  1. 根据价格筛选数据

将筛选出的数据重新渲染到表中

function setData(ary) {

// 先删除以前的数据

tbody.innerHTML = ‘’

ary.forEach(function (item) {

var tr = `

${item.id} ${item.title} ${item.price} `

tbody.insertAdjacentHTML(‘beforeend’, tr)

})

}

// 根据价格过滤商品

var btnPrice = document.querySelector(‘#btnPrice’)

var min_price = document.querySelector(‘#min_price’)

var max_price = document.querySelector(‘#max_price’)

btnPrice.addEventListener(‘click’, function () {

var result = fruits.filter(function (item) {

return item.price >= min_price.value && item.price <= max_price.value

})

// 重新渲染数据

setData(result)

})

  1. 根据名称搜索数据

// 根据名称搜索

var btnTitle = document.querySelector(‘#btnTitle’)

var title = document.querySelector(‘#title’)

btnTitle.addEventListener(‘click’, function () {

var result = fruits.filter(function (item) {

return item.title == title.value

})

// 重新渲染表格

setData(result)

})

上面数组中,水果名称是唯一的

使用filter会便利每一个对象,然后比较,查询效率相对较低

find 方法只要查询到一个符合条件的就会停止,所以效率相对较高

btnTitle.addEventListener(‘click’, function () {

// find 方法返回的不是数组,而是对象,所以要使用foreach,需要将

// 返回的对象放到数组中

var result = fruits.find(function (item) {

console.log(item)

return item.title == title.value

})

var arr = []

arr.push(result)

// 重新渲染表格

setData(arr)

})

完整代码

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

分享一套阿里大牛整理的前端资料给大家,点击前端校招面试题精编解析大全即可免费下载

❤️ 谢谢支持,喜欢的话别忘了 关注、点赞哦。

染数据

setData(result)

})

  1. 根据名称搜索数据

// 根据名称搜索

var btnTitle = document.querySelector(‘#btnTitle’)

var title = document.querySelector(‘#title’)

btnTitle.addEventListener(‘click’, function () {

var result = fruits.filter(function (item) {

return item.title == title.value

})

// 重新渲染表格

setData(result)

})

上面数组中,水果名称是唯一的

使用filter会便利每一个对象,然后比较,查询效率相对较低

find 方法只要查询到一个符合条件的就会停止,所以效率相对较高

btnTitle.addEventListener(‘click’, function () {

// find 方法返回的不是数组,而是对象,所以要使用foreach,需要将

// 返回的对象放到数组中

var result = fruits.find(function (item) {

console.log(item)

return item.title == title.value

})

var arr = []

arr.push(result)

// 重新渲染表格

setData(arr)

})

完整代码

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-dYCsFJOq-1712372281048)]

[外链图片转存中…(img-PW00CNu9-1712372281049)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-pMm5ZnxU-1712372281049)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

最后

分享一套阿里大牛整理的前端资料给大家,点击前端校招面试题精编解析大全即可免费下载

❤️ 谢谢支持,喜欢的话别忘了 关注、点赞哦。

前端校招面试题精编解析大全

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值