JavaScript高级学习笔记—Day02

目录

构造函数和原型

        一、概念

        创建对象可以通过以下三种方式:

        二、构造函数        

构造函数原型对象prototype

对象原型__proto__

constructor构造函数

构造函数、实例对象和原型对象之间的关系

原型链

原型对象中的this指向问题

原型对象扩展内置对象

继承

call()函数的使用

借用构造函数继承父类属性

借用原型对象继承父类的方法

类的本质

        语法糖

ES5中新增的方法


构造函数和原型

        一、概念

        在典型的 OOP 语言中(如 Java),都存在类的概念,就是对象的模板对象就是类的实例,但在 ES6之前, JS 中并没用引入类概念。ES6, 全称 ECMAScript 6.0 2015.06 发版但是目前浏览器JS ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 部分特性和功能。

        在 ES6之前 ,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。

        创建对象可以通过以下三种方式:

                1. 对象字面量

// 字面量创建对象
var obj = {}

                2.new Object()

// 利用 new Object() 创建对象
var obj = new Object();

                3. 自定义构造函数

// 创建构造函数
function Star(uname,age){
    this.uname = uname;
    this.age = age;
    this.sing = function(){
        console.log('正在唱歌!')
    }
}

// 实例化构造函数

const ldh = new Star('刘德华',18);
console.log(ldh);

        二、构造函数        

构造函数是把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面

        在 JS 中,使用构造函数时要注意以下两点:

                1.构造函数用于创建某一类对象首字母要大写

                2.构造函数要和 new 一起使用才有意义

        new 在执行时会做四件事情

                ①在内存中创建一个新的空对象

                ② this 指向这个新的对象。

                ③执行构造函数里面的代码给这个新对象加属性和方法。

                返回这个新对象(所以构造函数里面不需要 return

JavaScript 的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的 this 上添加通过这两种方式添加的成员,就分别称为静态成员实例成员

静态成员:在构造函数本身上添加的成员称为静态成员只能由构造函数本身来访问

实例成员:构造函数内部创建的对象成员实例成员只能由实例化的对象来访问

构造函数原型对象prototype

        因为构造函数方法中存在浪费内存的问题。即:在构造函数中的方法,每次的实例化对象的时候,对象的方法都是重新开辟内存空间,存在一定程度上的内存浪费的问题。

        构造函数通过原型分配的函数是所有对象所共享的。JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

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

        1. 原型是什么 ?  一个对象,我们也称为 prototype 原型对象。

        2. 原型的作用是什么 ?  共享方法

// 创建构造函数
function Star(uname,age){
    this.uname = uname;
    this.age = age;
}

Star.prototype.sing = function(){
        console.log('正在唱歌!')
}

// 实例化构造函数

const ldh = new Star('刘德华',18);
const zxu= new Star('张学友',20);
console.log(ldh.sing() === zxu.sing());    // 返回的是 true

对象原型__proto__

        对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。

        __proto__对象原型和原型对象 prototype 等价的

        __proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

tip:   对象原型__proto__ 和 原型对象 prototype 为 实例对象 在查找方法上提供了方向,即:如果在实例对象上有给方法就直接用,若找不到该方法,那么就会通过 __proto__ 去到原型对象 prototype 身上查找对应的方法。__proto__的意义在于为对象查找方法提供一条查找方向。

constructor构造函数

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

        constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。(这就方便我们在某些情况下,手动利用constructor指向原来的构造函数)

        一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor  就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

// 创建构造函数
function Star(uname,age){
    this.uname = uname;
    this.age = age;
}

// 这种情况下,构造函数会被覆盖掉,这时需要我们手动指明构造函数
//Star.prototype = {
//    sing: function(){
//        console.log('正在唱歌!')
//    },
//    movie: function(){
//        console.log('正在演戏!')
//    }
//}

Star.prototype = {
    constructor: Star,
    sing: function(){
        console.log('正在唱歌!')
    },
    movie: function(){
        console.log('正在演戏!')
    }
}
// 实例化构造函数

const ldh = new Star('刘德华',18);
const zxu= new Star('张学友',20);
console.log(ldh.sing() === zxu.sing());    // 返回的是 true

构造函数、实例对象和原型对象之间的关系

原型链

        在构造函数中有一条对象原型与原型对象所构成的关系链。 如图所示:构造函数Star的实例对象ldh如果要找要调用的函数,自身没有那么会通过它的对象原型__proto__找到原型对象prototype,如果原型对象身上也没有,那么它会找到Object构造函数身上的原型对象prototype,如果还是没有找到,那么会找到Object身上的对象原型__proto__,而Object身上的对象原型为null。(这是原型链的大致走向)

原型对象中的this指向问题

        构造函数中的this 指向我们实例对象。原型对象里面放的是方法这个方法里面的this 指向的是 这个方法的调用者, 也是实例对象.

原型对象扩展内置对象

        可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求和的功能。注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} 这样会报错,只能是 Array.prototype.xxx = function(){} 的方式。

Array.prototype.sum = function(){
    let sum = 0;
    for(var i = 0; i < this.length; i++){
        sum += this[i];
    }
    return sum;
}

// 这样会覆盖Array原型身上的所有方法,控制台会报错
//Array.prototype = {
//    sum: function(){
//      let sum = 0;
//        for(var i = 0; i < this.length; i++){
//            sum += this[i];
//        }
//    return sum;
//    }
//}
console.log(Array.prototype)
a1 = [1,2,3]
console.log(a1.sum)        //控制台会打印出 6

继承

        ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,称为组合继承

call()函数的使用

        调用这个函数, 并且修改函数运行时的 this 指向  

fun.call(thisArg, arg1, arg2, ...) 

// thisArg :当前调用函数 this 的指向对象
// arg1,arg2:传递的其他参数

call使用小栗子:

function fn(){
    console.log("我想吃小蛋糕!");
    
    console.log(this);
}

let o = {
    name: "钮枯禄"
}

// 使用call调用函数
fn.call()        // 控制台会打印出  我想吃小蛋糕! 和  window对象

// 改变fn的this指向
fn.call(o)    // 控制台会打印出  我想吃小蛋糕! 和  对象o

借用构造函数继承父类属性

        核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。  

// 父类
function Father(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
// 子类
function Son(name, age, sex, score) {
    Person.call(this, name, age, sex);  // 此时父类的 this 指向子类的 this,同时调用这个函数
    this.score = score;
}
    var s1 = new Student('钮枯禄', 18, '女', 100);
    console.log(s1); 

借用原型对象继承父类的方法

        一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。 

         核心原理:

                ①将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类() 

                ②本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象

                将子类的 constructor 从新指向子类的构造函数

// 父类
function Father(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}

Father.prototype.dream = function(){
    console.log('暴富·暴瘦·好运暴');
}
// 子类
function Son(name, age, sex, score) {
    Person.call(this, name, age, sex);  // 此时父类的 this 指向子类的 this,同时调用这个函数
    this.score = score;
}

// Son.prototype = Father.prototype //这种方法,在后期给Son原型上添加方法会影响到Father中的原型

Son.prototype = new Father();
Son.prototype.constructor = Son;

var s1 = new Student('钮枯禄', 18, '女', 100);
console.log(s1); 

类的本质

        的本质还是构造函数;类的所有方法都定义在类的prototype属性上;类创建的实例,里面也有__proto__ 指向类的prototype原型对象;所以ES6类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已

        所以ES6的类其实就是语法.

tip: 

        语法糖

        语法糖就是一种便捷写法.   简单理解, 有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便,那么这个方法就是语法糖。

ES5中新增的方法

        ES5 中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:数组方法、字符串方法对象方法。

数组方法

        迭代(遍历)方法:forEach()、map()、filter()、some()、every();

// currentValue:数组当前项的值 
// index:数组当前项的索引
// arr:数组对象本身

array.forEach(function(currentValue, index, arr))    


// filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组    注意它直接返回一个新数组
// currentValue: 数组当前项的值
// index:数组当前项的索引
// arr:数组对象本身

array.filter(function(currentValue, index, arr))

arr = [11,22,44,55,46,23]
var newArr = arr.filter(function(value,index){
    return value % 2 == 0;
})
console.log(newArr);    //控制台输出的是 [22,44,46]

// some() 方法用于检测数组中的元素是否满足指定条件.   通俗点 查找数组中是否有满足条件的元素 
// 注意它返回值是布尔值, 如果查找到这个元素, 就返回true ,  如果查找不到就返回false.
// 如果找到第一个满足条件的元素,则终止循环. 不在继续查找.
// currentValue: 数组当前项的值
// index:数组当前项的索引
// arr:数组对象本身

array.some(function(currentValue, index, arr))

arrSome = [11,22,44,55,46,23]
var newArrSome = arrSome .some(function(value){
    return value % 2 == 0;
})
console.log(newArrSome);    //控制台输出的是 true

        filter()和some()都是筛选数组中满足条件的元素不同的是filter返回的是满足条件的所有元素,some返回的是满足条件的元素是否存在,找到第一个满足条件的后面都不看了直接返回true,返回的是一个boolean值。

Tip:   如果要查询数组中唯一的元素,那么使用some()方法是效率最高的。其中在forEacher()和filter()中,使用return true;是不会终止程序的。只有some会终止程序,这也是some效率高的原因。

字符串方法

        trim()  方法会从一个字符串的两端删除空白字符;trim() 方法并不影响原字符串本身,它返回的是一个新的字符串

str.trim()    //返回的是一个新的字符串

对象方法

        1.  Object.keys() 用于获取对象自身所有的属性    Object.keys(obj)

                效果类似 for…in;返回一个由属性名组成的数组

// Object.keys(obj)

var obj = {
    id: 1,
    pname: '小米',
    price: 1999
}

console.log(Object.keys(obj))    //结果是一个数组  ['id','pname','price']

        2.  Object.defineProperty() 定义对象中新属性或修改原有的属性

// obj:必需。目标对象 
// prop:必需。需定义或修改的属性的名字
// descriptor:必需。目标属性所拥有的特性(以对象形式 { } 书写)
// descriptor 的特性:
    // value: 设置属性的值  默认为undefined
    // writable: 值是否可以重写。true | false  默认为false
    // enumerable: 目标属性是否可以被枚举。true | false 默认为 false
    // configurable: 目标属性是否可以被删除或是否可以再次修改特性 true | false  默认为false


Object.defineProperty(obj, prop, descriptor)

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BlackStar-Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值