一、构造函数
1.1 概述
1.2 对象的三种创建方式(复习)
-
字面量方式 var obj = {}; -
new关键字 var obj = new Object(); -
构造函数方式 function Person(name,age){
?this.name = name;
?this.age = age;
}
var obj = new Person('zs',12);
1.3 构造函数
?
// 3. 利用构造函数创建对象
function Star(uname, age) {
? ?this.uname = uname;
? ?this.age = age;
? ?this.sing = function() {
? ? ? ?console.log('我会唱歌');
?
? }
}
?
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh);
ldh.sing();
zxy.sing();
1.4 静态成员和实例成员
成员:属性和方法。
实例成员
实例成员就是构造函数内部通过this添加的成员,
如下列代码中:uname age sing 就是实例成员,
实例成员只能通过实例化的对象来访问
// 构造函数中的属性和方法我们称为成员, 成员可以添加
function Star(uname, age) {
? ? this.uname = uname;
? ? this.age = age;
? ? this.sing = function() {
? ? console.log('我会唱歌');
? }
}
var ldh = new Star('刘德华', 18);
console.log(ldh.uname);//实例成员只能通过实例化的对象来访问
静态成员
构造函数本身也是1个对象!
静态成员:在构造函数本身上添加的成员。
如下列代码中 sex 就是静态成员,静态成员
只能通过构造函数来访问
function Star(uname, age) {
? ? this.uname = uname;
? ? this.age = age;
? ? this.sing = function() {
? ? console.log('我会唱歌');
? }
}
// 静态成员 在构造函数本身上添加的成员 sex 就是静态成员
Star.sex = '男';
var ldh = new Star('刘德华', 18);
console.log(Star.sex);//静态成员只能通过构造函数来访问
二、原型对象
2.1 构造函数的问题
构造函数创建对象很好用,但是如果把方法定义在构造函数内,就存在浪费内存的问题。
我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上。
2.2 构造函数的原型prototype
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象,这个对象叫做【原型对象】。
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
function Star(uname, age) {
? ?// 构造函数中定义属性
? ?this.uname = uname;
? ?this.age = age;
}
// 把方法定义到原型对象身上
Star.prototype.sing = function() {
? ?console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
?
ldh.sing();
zxy.sing();
?
// 所有的实例对象,共用一个方法,省内存
console.log(ldh.sing === zxy.sing);
2.3 对象原型
实例对象都有一个属性 __proto__ 指向构造函数的 prototype 原型对象。
?
之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。
?
__proto__对象原型和原型对象 prototype 是等价的。
?
__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype。
?
?
2.4 constructor构造函数
?
如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数如:
function Star(uname, age) {
? ? this.uname = uname;
? ? this.age = age;
}
// 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
Star.prototype = {
// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
? constructor: Star, // 手动设置指回原来的构造函数
? sing: function() {
? ? console.log('我会唱歌');
? },
? movie: function() {
? ? console.log('我会演电影');
? }
}
var zxy = new Star('张学友', 19);
2.5 三角关系
构造函数、实例对象和原型对象的三角关系。
1.构造函数的prototype属性指向了构造函数原型对象
2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象
3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数
四、原型链与对象成员的查找规则
4.1 原型链
<!--任何原型对象也是一个对象,该对象就有 __proto__ 属性,这样一层一层往上找,就形成了一条链,我们称此为原型链;-->
4.2 对象成员的查找机制
当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
?
如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
?
如果还没有就查找:原型对象的原型(Object的原型对象)。
?
依此类推一直找到 Object 为止(null)。
沿着原型链查找!!
五、原型对象的应用
5.1 原型对象函数中this指向
不管构造函数中的this,还是原型对象中的this,都指向我们new出来的实例对象。
<script>
? ?function Star(uname, age) {
? ? ? ?this.uname = uname;
? ? ? ?this.age = age;
? }
? ?var that;
? ?Star.prototype.sing = function() {
? ? ? ?console.log('我会唱歌');
? ? ? ?that = this;
? }
? ?var ldh = new Star('刘德华', 18);
? ?// 1. 在构造函数中,里面this指向的是对象实例 ldh
? ?ldh.sing();
? ?console.log(that === ldh);
?
? ?// 2.原型对象函数里面的this 指向的是 实例对象 ldh
</script>
5.2 通过原型为数组扩展内置方法
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
// 系统中内置了很多构造函数:例如:Object、Array
// Array构造函数也有一个对应的原型对象
Array.prototype.sum = function() {
? ?var sum = 0;
? ?for (var i = 0; i < this.length; i++) {
? ? ? ?sum += this[i];
? }
? ?return sum;
};
// 数组的实例对象 arr
var arr = [1, 2, 3];
// 调用sum方法(成员的查找规则)
console.log(arr.sum());
console.log(Array.prototype);
?
// 数组的实例对象 arr1
var arr1 = new Array(11, 22, 33);
console.log(arr1.sum());
六、继承
子构造函数 继承 父构造函数!
6.1 call()方法
调用这个函数, 并且修改函数运行时的 this 指向 。
function fn(x, y) {
? ? console.log('我想喝手磨咖啡');
? ? console.log(this);
? ? console.log(x + y);
}
?var o = {
name: 'andy'
};
?// fn();
?// 1. call() 可以调用函数
?// fn.call();
?// 2. call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
?fn.call(o, 1, 2);
6.2 继承属性
-
先定义一个父构造函数 -
再定义一个子构造函数 -
子构造函数继承父构造函数的属性(使用call方法)
// 1. 父构造函数
function Father(uname, age) {
? // this 指向父构造函数的对象实例
? this.uname = uname;
? this.age = age;
}
?// 2 .子构造函数
function Son(uname, age, score) {
?// this 指向子构造函数的对象实例 ldh
?// 3.使用call方式实现子继承父的属性【调用父构造函数,并让其this变为ldh】
?Father.call(this, uname, age);
?this.score = score;
}
var ldh = new Son('刘德华', 18, 100);
console.log(son);
6.3 继承方法
借用原型对象继承方法!
-
先定义一个父构造函数 -
再定义一个子构造函数 -
修改子构造函数的原型对象(new 父构造函数得到的对象)
// 1. 父构造函数
function Father(uname, age) {
?// this 指向父构造函数的对象实例
?this.uname = uname;
?this.age = age;
}
Father.prototype.money = function() {
?console.log(100000);
};
// 2 .子构造函数
?function Son(uname, age, score) {
? ? ?// this 指向子构造函数的对象实例
? ? ?Father.call(this, uname, age);
? ? ?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);
七、类的本质
?
?
<script>
? ?// ES6 之前通过 构造函数 + 原型 实现面向对象编程
? ?// (1)构造函数有原型对象prototype
? ?// (2)构造函数原型对象prototype里面有constructor 指向构造函数本身
? ?// (3)构造函数可以通过原型对象添加方法
? ?// (4)构造函数创建的实例对象有__proto__原型指向 构造函数的原型对象
?
? ?// ES6 通过 类 实现面向对象编程
? ?class Star {
?
? }
? ?console.log(typeof Star);
? ?// 1. 类的本质其实还是一个函数,我们也可以简单的认为 类就是 构造函数的另外一种写法
? ?// (1)类有原型对象prototype
? ?console.log(Star.prototype);
? ?// (2)类 也有原型对象,这个原型对象指向类本身
? ?console.log(Star.prototype.constructor);
? ?// (3)类 可以通过原型对象添加方法
? ?Star.prototype.sing = function() {
? ? ? ?console.log('冰雨');
? };
? ?// (4)类创建的实例对象,有__proto__原型,指向类的原型对象
? ?var ldh = new Star();
? ?console.dir(ldh);
? ?console.log(ldh.__proto__ === Star.prototype);
</script>
八、ES5新增方法
8.1 数组方法forEach遍历数组
arr.forEach(function(value, index, array) {
? ? ? //参数一是:数组元素
? ? ? //参数二是:数组元素的索引
? ? ? //参数三是:当前的数组
})
?//相当于数组遍历的 for循环 没有返回值
8.2 数组方法filter过滤数组
?var arr = [12, 66, 4, 88, 3, 7];
?var newArr = arr.filter(function(value, index,array) {
//参数一是:数组元素
? ? //参数二是:数组元素的索引
? ? //参数三是:当前的数组
? ? return value >= 20;
});
?console.log(newArr);//[66,88] //返回值是一个新数组
8.3 数组方法some
some 查找数组中是否有满足条件的元素
var arr = [10, 30, 4];
var flag = arr.some(function(value,index,array) {
? ?//参数一是:数组元素
? ? //参数二是:数组元素的索引
? ? //参数三是:当前的数组
? ? return value < 3;
});
console.log(flag);//返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环
|