构造函数和原型 JavaScript

构造函数和原型

构造函数和原型

概述

在典型的 OOP的语言中(如Java ),都存在类的概念,类就是对象的模板,对象就是的实例,但在 ES6之前,JS 中并没有引入类的概念。

构造函数

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面

new 在执行时会做四件事情:

  1. 在内存中创建一个新的空对象
  2. 让this 指向这个新的对象。
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法
  4. 返回这个新对象(所以构造函数里面不需要 return )

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

  • 静态成员:在构造函数本上添加的成员称为静态成员,只能由构造函数本身来访问
  • 实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问
构造函数的问题

构造函数方法很好用,但是存在浪费内存的问题

我们希望所有的对象使用同一个函数,这样就比较节省内存,那么我们要怎样做呢 ?

构造函数原型 prototype

构造函数通过原型分配的函数是所有对象所共享的

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

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

问答?

  1. 原型是什么 ?

    一个对象,我们也称为 prototype 为原型对象

  2. 原型的作用是什么?

    共享方法

对象原型 __ proto__

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

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

constructor 构造函数

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

constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数

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

原型链

JavaScript 的成员查找机制(规则)
  1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
  2. 如果没有就查找它的原型(也就是 __ proto__ 指向的prototype 原型对象)
  3. 如果还没有就查找原型对象的原型(Object的原型对象)
  4. 依此类推一直找到Obiect为止(null)。
  5. __ proto__ 对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
原型对象 this 指向
  1. 在构造函数中,里面 this 指向的是对象实例
  2. 原型对象函数里面的 this 指向的是 实例对象
扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能

注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} ,只能是 Aray.prototype.xxx = function0{} 的方式

继承

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

call()

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

fun.call(thisArg, argl, arg2, ...);
  • thisArg:当前调用函数 this 的指向对象
  • arg1,arg2:传递的其他参数
借用构造函数继承父类型属性

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

借用构造函数继承父类型方法

核心原理:通过实例化父类型,通过 __ proto__ 的继承性获取父类型方法,然后 constructor 指向子类型防止被覆盖

// 借用构造函数继承父类型属性
// 1. 父构造函数
function Father(umane, age) {
    // this 指向父构造函数的对象实例
    this.uname = uname;
    this.age = age;
}
Father.prototype = {
    money: {
        console.log(1000);
    }
}
// 2.子构造函数
function Son(uname, age, score){
    // this 指向子构造函数的对象实例
    Father,call(this, uname, age);  // 这里 this 指的是 Son 等同于 Son
    this.score = score;
}
// Son.prototype = Father.prototype;  注意直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化,因为赋值了父原型的地址
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;
// 这个是子构造函数专门的方法
Son.prototype.exam = function() {
    console.log('孩子要考试');
}
var son = new Son('刘德华', 18, 100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);

类的本质

  1. class本质还是 function.
  2. 类的所有方法都定义在类的 prototype 属性上
  3. 类创建的实例,里面也有 __ proto__ 指向类的 prototype 原型对象
  4. 所以ES6的类它的绝大部分功能,ES5都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
  5. 所以ES6的类其实就是语法糖
  6. 语法糖:语法糖就是一种便捷写法.简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖
// ES6 之前通过 构造函数+ 原型实现面向对象 编程
// (1)构造函数有原型对象prototype
// (2)构造函数原型对象prototype 里面有constructor 指向构造函数本身
// (3)构造函数可以通过原型对象添加方法
// (4)构造函数创建的实例对象有 __proto__原型指向 构造函数的原型对象
// ES6 通过 类 实现面向对象编程 
class Star {}
console.log(typeof Star);
// 1.类的本质其实还是一个函数 我们也可以简单的认为 类就是 构造函数的另外一种写法
// (1) 类有原型对象prototype
console.log(Star.prototype);
// (2) 类原型对象prototype 里面有constructor 指向类本身
console.log(Star.prototype.constructor);
// (3) 类可以通过原型对象添加方法
Star.prototype.sing = function() {
    console.log("冰雨');
}
var ldh = new star();
console.dir(ldh);
// (4) 类创建的实例对象有__proto__  原型指向 类的原型对象
consloe.log(ldh.__proto__ === Star.prototype);
// i = i + i; i++

ES5 中的新增方法

ES5 新增方法概述

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

  • 数组方法
  • 字符串方法
  • 对象方法
数组方法

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

array.forEach(function (currentvalue, index, arr));
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
array.filter(function(currentvalue, index, arr));
  • filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素 ,主要用于筛选数组
  • 注意它直接返回一个新数组
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
array.some (function(currentValue, index, arr));
  • some() 方法用于检测数组中的元素是否满足指定条件.通俗点查找数组中是否有满足条件的元素
  • 注意它返回值是布尔值, 如果查找到这个元素,就返回 true,如果查找不到就返回 false
  • 如果找到第一个满足条件的元素,则终止循环不在继续查找
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
// forEach 迭代(遍历) 数组
var arr = [1, 2, 3];
var sum = 0;
arr.forEach(function(value, index, array) {
    console.log('每个数组元素' + value);
    console.log('每个数组元素的索引号' + index);
    console.log('数组本身' + array);
    sum += value;
});
console.log(sum);

// filter 筛选数组
var arr = [12, 66, 4, 88, 3, 7];
var newArr = arr.filter(function(value, index) {
    // return value >= 20;
    return value % 2 === 0;
});
console.log(newArr);

// some 查找数组中是否有满足条件的元素
// var arr = [10, 30, 4];
// var flag = arr.some(function(value) {
//	// return value >= 20;
//	return value < 3;
// );
// console.log(flag);
var arr1 = ['red', 'pink','blue'];
var flag1 = arr1.some(function(value) {
    return value == 'pink';
});
console.log(flag1);
// 1.fiter 也是查找满足条件的元素 返回的是一个数组 而且是把所有满足条件的元素返回回来
// 2.some 也是查找满足条件的元素是否存在 返回的是一个布尔值 如果查找到第一个满足条件的元素就终止循环
字符串方法

trim() 方法会从一个字符串的两端删除空白字符

str.trim();

trim() 方法并不影响原字符串本身,它返回的是一个新的字符串

对象方法

2.Object.defineProperty0定义新属性或修改原有的属性

Object.defineProperty(obj,prop, descriptor);
  • oj:必需。目标对象
  • prop:必需。需定义或修改的属性的名字
  • descriptor:必需。目标属性所拥有的特性

Object.defineProperty () 第三个参数descriptor说明:以对象形式 {} 书写

  • value:设置属性的值 默认为undefined
  • writable:值是否可以重写。true | false 默认为false
  • enumerable:目标属性是否可以被枚举。true | false 默认为false
  • confiqurable:目标属性是否可以被删除或是否可以再次修改特性true | false 默认为false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值