文章目录
一、构造函数和原型
1. 概念:
在典型的OOP语言中, 都存在类的概念, 类就是对象的模板, 对象就是类的实例. 在ES6之前, JS中并没有类的概念;
ES6, 全称ECMAScript 6.0, 2015.06发版. 但是目前浏览器的JavaScript事ES5, 大多数高版本的浏览器也支持ES6, 只不过实现了ES6的部分特性和功能;
在ES6之前, 对象不是基于类创建的, 而是用一种称为构造函数的特殊函数来定义对象和他们的特征;
创建对象可用通过以下三种方式:
- 对象字面量;
var obj1 = new Object();
- new Object();
var obj2 = {}
- 自定义构造函数;
function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log(this.uname + ' 唱歌...') } } var ldh = new Star('刘德华', 20); ldh.sing(); // 刘德华 唱歌...
2. 构造函数:
构造函数是一种特殊的函数, 主要用来初始化对象, 即为对象成员变量付初始值. 它总与new一起使用. 可以把对象中一些公共的属性和方法抽取出来, 然后封装到这个函数里面;
在JS中, 使用构造函数时要注意一下两点:
- 构造函数用于创建某一对象, 其首字母要大写;
- 构造函数要和new一起使用才有意义;
new在执行时会做四件事情:
- 在内存中创建一个新的空对象;
- 让this指向这个新对象;
- 执行构造函数里面的代码, 给这个新对象添加属性和方法;
- 返回这个新对象(所以构造函数里面不需要return);
3. 静态成员与实例成员:
JavaScript的构造函数中可以添加一些成员, 可以在构造函数本身上添加, 也可以在构造函数内部的this上添加. 通过这两种方式添加的成员, 就分别称为静态成员和实例成员
- 静态成员: 在构造函数本身上添加的成员称为静态成员. 只能由构造函数本身访问;
- 实例成员: 在构造函数内部创建的对象成员称为实例成员. 只能由示例化的对象来访问,通过this来添加的 ;
function Star(uname, age) {
this.uanem = uanme;
thia.age = age;
}
console.log(Star);
/*
ƒ Star(uname, age) {
this.uanem = uanme;
thia.age = age;
}
*/
Star.sex = "男";
console.log(Star.sex); // 男
4. 原型:
4.1 构造函数的问题:
- 存在浪费内存的问题:
4.2 原型prototype:
构造函数通过原型分配的函数是所有对象所共享的;
JavaScript规定, 每一个构造函数都有一个prototype属性, 指向另一个对象. 注意这个prototype就是一个对象, 这个对象的所有属性和方法, 都会被构造函数所拥有;
可以把那些不变的方法, 直接定义的prototype对象上, 这样所有的对象的实例就可以共享这些方法了;
一般情况下, 公共属性订到构造函数中; 公共方法放到原型对象上;
function Star(uname, age) {
this.uanem = uname;
this.age = age;
}
console.dir(Star);
function Star(uname, age) {
this.uanem = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log("我会唱歌...");
}
// Star.sing(); // 错误
var ldh = new Star("刘德华", 20);
ldh.sing();
4.3 对象原型__proto__:
**对象都会有一个__proto__**指向构造函数的prototype原型对象, 对象之所以可以使用prototype原型对象的属性和方法, 就是因为对象有__proto__原型存在;
- __proto__对象原型和prototype原型对象是等价的;
- __proto__对象原型的意义就在于对象的查找机制提供一个方向, 或者说一条路线, 但是它是一个非标准熟悉感, 因此,实际开发中, 不可以使用这个属性, 它只是内部指向原型对象prototype;
console.log(ldh);
方法的查找规则:
- 先看对象身上是否有方法, 如果有就执行这个对象上的方法;
- 如果没有这个方法, 因为有__proto__的存在, 就去构造函数原型对象prototype身上找这个方法;
4.4 构造函数 constructor:
对象原型(proto)和原型对象(prototype)里面都有一个constructor属性, 被称为构造函数, 因为它指回构造函数本身;
function Star(uname, age) {
this.uanem = uname;
this.age = age;
}
var ldh = new Star("刘德华", 20);
console.log(ldh.__proto__);
console.log(Star.prototype);
console.log(ldh.__proto__.constructor);
console.log(Star.prototype.constructor);
// prototype使用对象赋值, 需要手动利用constructor指回原来的构造函数
Star.prototype = {
// 手动添加构造函数
constructor: Star,
sing: function() {
console.log("我会唱歌...");
},
movie: function() {
console.log("我会演电影...");
}
}
console.log(ldh.__proto__);
console.log(Star.prototype);
console.log(ldh.__proto__.constructor);
console.log(Star.prototype.constructor);
4.5 构造函数、实例、原型对象三者之间的关系:
4.6 原型链:
JavaScript的成员查找机制:
- 当访问一个对象的属性(或方法)时, 首先查找这个对象自身有没有这个属性(或方法);
- 如果没有就查找它的原型(也就是__proto__指向的prototype对象);
- 如果还没有, 就查找原型对象的原型(Object的原型对象);
- 依次类推一直到Object为止(null);
4.7 原型对象this指向:
- 构造函数中, 里面的this指向的是对象实例;
- 原型对象函数里面的this指向的是实例对象;
4.8 扩展内置对象:
可以通过原型对象, 对原来的内置对象进行扩展自定义的方法. 比如: 给数组增加自定义求偶数和的功能;
console.log(Array.prototype);
// 增加自定义
Array.prototype.sum = function(){
let sum = 0;
for(let i = 0; i < this.length; i++){
sum += this[i]
}
return sum
}
var arr = [1,2,3,5]
console.log(arr.sum()); // 11
二、 继承:
ES6之前并没有提供extends继承. 可以通过构造函数+原型模拟实现继承, 被称为组合继承
1 call():
调用这个函数, 并且修改函数运行时的this指向:
func.call(thisArg, arg1, arg2)
- thisArg: 当前调用函数this的指向函数;
- arg1, arg2, …: 传递的其他参数
2 借用构造函数继承父类属性:
核心原理: 通过call()把父类型的this指向给子类型的this, 这样就可以实现子类型继承父类型的属性了;
// 1. 父构造函数
function Father(uname, age) {
this.uname = uname;
this.age = age;
}
// 2. 子构造函数
function Son(uname, age) {
Father.call(this, uname, age)
}
var son = new Son('张三', 10);
console.log(son.uname); // 张三
3. 借用构造函数继承父类方法:
// 1. 父构造函数
function Father(uname, age) {
this.uname = uname;
this.age = age;
}
// 如果不写在了原型对象中, 则不能被call继承
Father.prototype.money = function() {
console.log("赚钱...");
}
// 2. 子构造函数
function Son(uname, age) {
Father.call(this, uname, age)
}
// 如果利用对象赋值的方式修改了原型, constructor指回原来的构造函数
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.exam = function() {
console.log("孩子要考试...");
}
var son = new Son('张三', 10);
console.log(son);
console.dir(Father);
三、ES5中的新增方法:
1. 数组方法:
- 迭代方法: forEach()、map()、filter()、some()、every()
1.1 forEach:
array.forEach(function(currentValue, index, arr))
- currentValue: 数组当前项的值;
- index: 数组当前项的索引;
- arr: 数组本身;
var arr = [1,2,3,4,5,6,7,8,9,0]
arr.forEach((currentValue, index, arr) => {
console.log(currentValue);
console.log(index);
console.log(arr);
});
1.2 filter:
filter()创建一个新的数组, 新数组中的元素是通过检测指定数组中符合条件的所有元素,主要用于筛选数组
array.filter(function(currentValue, index, arr))
- currentValue: 数组当前项的值;
- index: 数组当前项的索引;
- arr: 数组本身;
var f_arr = arr.filter((currentValue, index, arr) => {
return currentValue % 2 == 0;
})
console.log(f_arr); // [2, 4, 6, 8, 0]
1.3 some:
some()用于检测数组中的元素是否满足指定条件, 通俗点查找数组中是否有满足条件的值;
注意: 它返回值是布尔值, 如果查找到这个元素, 就返回true; 如果查找不到就返回false;
如果找到第一个满足条件的元素, 就终止循环,返回true; 不存在则返回false;
array.some(function(currentValue, index, arr))
- currentValue: 数组当前项的值;
- index: 数组当前项的索引;
- arr: 数组本身;
var res = arr.some((currentValue) => {
return currentValue % 2 == 0;
})
console.log(res); // true
2. 字符串方法:
trim()会从一个字符串的两端去除空格;
trim()并不影响原字符串本身, 而是返回一个新的字符串;
str.trim()
3. 对象方法:
3.1 Object.keys():
Object.keys()用于获取对象自身的所有属性:
Object.keys()
返回一个有属性名组成的数组;
3.2 Object.defineProperty():
Object.defineProperty()定义对象中新属性或修改原有属性
Object.defineProperty(obj, prop, descripttor)
- obj: (必须)目标对象;
- prop: (必须) 新定义或修改的属性的名字;
- descriptor: (必须) 以对象的形式, 目标属性所拥有的特性;
descriptor 参数说明:
属性 | 说明 |
---|---|
value | 设置属性值, 默认为undefined |
writable | 值是否可重写(可被遍历). 默认: false, enumerable:false |
enumerable | 目标属性是否可以被枚举(被遍历);默认: false |
configurable | 目标属性是否可以被删除 或 是否可以被再次修改特性; 默认: false |
var obj = {
id: 1,
pname: "小米",
price: 1999
}
// 添加 or 修改
Object.defineProperty(obj, 'num', {
value: 1000
})
// 限定属性
Object.defineProperty(obj, 'id', {
writable: false // 不可修改, 默认enumerable: false 不可被遍历
})
obj.id = 3; // 不生效, 不报错
console.log(obj);
// enumerable
Object.defineProperty(obj, 'address', {
value: '北京市昌平区',
enumerable: false // 不可被遍历
})
console.log(Object.keys(obj));
// 不允许删除 或 修改特性
Object.defineProperty(obj, 'pname', {
configurable: false
})
delete obj.pname
console.log(obj);