持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情
工厂函数
创建工厂函数
function creatObj(width,color,brand,lang){
// 属性 - 构造属性
this.width = width; // this
this.color = color;
this.brand = brand;
this.lang = lang;
// 方法 - 构造方法
this.run = function(){
console.log( this.brand + '在路上行驶');
}
this.start = function(){
console.log( this.brand + 'start');
}
this.stop = function(){
console.log(this.brand + '刹不住车了!');
}
var byd = new creatObj('1500mm','red','比亚迪');
var bmw = new creatObj('1500mm','白色','BMW');
bmw.start();
byd.start();
}
这样也可以创建一个对象,但是每次创建的对象的地址都是一个新的内存地址,内存地址是不同的
console.log(bmw.start == byd.start); // false
这样会更加浪费内存空间,需要讲方法放在原型中
// 工厂模式;
function creatObj(width,color,brand,lang){ // 类
// 属性 - 构造属性
this.width = width; // this
this.color = color;
this.brand = brand;
this.lang = lang;
// 方法 - 构造方法
this.run = function(){
console.log( this.brand + '在路上行驶');
}
}
// 原型 prototype - 属性
creatObj.prototype.lang = this.lang;
// 原型 prototype - 方法
creatObj.prototype.start = function(){
console.log( this.brand + 'start');
}
creatObj.prototype.stop = function(){
console.log(this.brand + '刹不住车了!');
}
var byd = new creatObj('1500mm','red','比亚迪'); // 对象
var bmw = new creatObj('1500mm','白色','BMW'); // 对象
bmw.start();
byd.start();
案例:给数组添加sum求和方法
正常添加
var arr = new Array(1,2,3,4,5,6,7);
// sum 字定义了一个方法
arr.sum = function(){
var res = 0;
for(var i=0; i<this.length; i++){
res += this[i];
}
return res;
}
console.log(arr.sum()); // 28
var arr2 = [1,2,3];
console.log(arr2.sum()); // undefined
当前sum方法只是添加给了arr这个对象,并没有添加给整个数组对象,所以如果是给已经存在的对象添加方法,需要使用prototype原型方法
var arr = new Array(1,2,3,4,5,6,7);
// sum 字定义了一个方法
Array.prototype.sum = function(){
var res = 0;
for(var i=0; i<this.length; i++){
res += this[i];
}
return res;
}
console.log(arr.sum()); // 28
var arr2 = [1,2,3];
console.log(arr2.sum()); // 6
原型相关的方法
- Object.getPrototypeOf() 取得实例的原型对象。
Object.getPrototypeOf(person1);
- isPrototypeOf() 判断是不是一个实例的原型对象。
Person.prototype.isPrototypeOf(person1);
- hasOwnProperty() 检测一个属性是否存在于实例中
person1.hasOwnProperty("name");
- in 判断一个属性是否存在于实例和原型中。
"name" in person1;
面向对象就是一种全新的写法而已。用面向过程能实现的功能,面向对象依然能够实现,对于初学者来说优点麻烦
思考:给数组添加去重方法
方法一:函数方式实现
function arrayNoRepeat(arr) {
var tempArr = [];
for (var i = 0; i < arr.length; i++) {
if (tempArr.indexOf(arr[i]) == -1) {
tempArr.push(arr[i]);
}
}
return tempArr;
}
var numArr = [9, 9, 3, 4, 8, 9, 3, 4, 8];
var relArr = arrayNoRepeat(numArr);
console.log(numArr);
console.log(relArr);
方法二:通过原型方法实现
Array.prototype.noRepeat = function () {
for (var i = 0; i < this.length; i++) {
for (var j = i + 1; j < this.length; j++) {
if (this[j] == this[i]) {
this.splice(j, 1);
j--;
}
}
}
}
console.log(numArr);
numArr.noRepeat();
console.log(numArr);
继承
面向对象语言的特点: 封装, 继承, 多态
面向对象中,有两个很重要的东西,属性和方法
所以继承的时候就是想办法把属性和方法继承过来就可以了
属性和方法
构造属性/方法
原型属性/方法
继承
构造继承
原型继承
1.继承-关键词
继承: 一个类拿到另一个类的属性/方法
JS 中的继承可以通过 call() 和 apply() 两个方法实现.call 和 apply 方法的本质是通过改变this指向来实现继承.
call语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])
apply语法:apply([thisObj[,argArray]]) argArray必须为数组格式
2. 构造继承
构造继承(如上述代码, 通过 call 和 apply 实现)
创建一个Student对象(姓名,性别,年龄,学号)
用.call()和.apply()将父类构造函数引入子类函数
特点:只能继承构造中的内容,不能继承原型中的内容
function Student(name,sex,age,stu_id){
// 继承方法1: 让student 继承 person
// Person.call(this,name,sex,age);
Person.apply(this,[name,sex,age]);
// 添加自己的构造属性
this.stuID = stu_id;
// 重写
this.play = function(){
console.log('蹦迪!!!');
}
// 添加学习方法
this.study = function(){
console.log('studey');
}
}
// 原型
Student.prototype.ID= '1000001';
Student.prototype.eat = function(){
console.log('减肥,不吃了');
}
3. 原型继承
实现的本质:重写原型对象,用一个新的类型的实例去替代
优点:
1, 简单粗暴的实现了继承, 非常纯粹的继承方式
2, 父类原型对象中的属性和方法都可以继承到
缺点:
1, 子类没法新增属性/方法
2, 无法实现多继承
var Student = function(){
}
Student.prototype = new Person();
var stu = new Student();
console.log(stu.ID); // 原型属性
stu.eat()// 原型方法
4. 实例继承
是一种变态的继承方式,通过在子类中实例化对象的形式,其实就是把父类给倒进来了
特点:
1, 不限制调用方式, 可以使用new 也可以不使用.
2, 子类实际上是父类的一个实例.
3, 不能实现多继承.
4, 不能给子类添加原型对象
function Student(name,sex,age){
var per = new Person(name,sex,age);
per.stuID= '123456789';
per.getId = function(){
console.log(this.stuID);
}
return per;
}
var stu = new Student('Jack','男',18);
stu.getId();
5. 拷贝继承
拷贝继承,并不是直接通过=进行赋值
特点:
1, 支持多继承
2, 效率低, 内存占用高
3, 无法获取父类中不可枚举的方法
function Student(name,sex,age,stuId){
var per = new Person(name,sex,age);
// 循环 - 将父级中的属性和方法进行遍历
for(var key in per){
// Student.name = jack
// Student[key] = per[key] 推的过程
Student.prototype[key] = per[key];
console.log(key); // 获取person中的键
console.log(per[key]); // 获取person中的值
}
Student.prototype.study = function(){
console.log('好好学习');
}
// 添加自己的属性
this.stu_id = stuId;
}
var stu = new Student('Jack','男',18,'1001');
console.log(stu);
6. 组合继承
用构造继承方法(call)继承父级的普通属性和方法; 用原型继承,继承父级的原型属性和方法 重点:结合了两种模式的优点,传参和复用。 特点:
1、可以继承父类原型上的属性,可以传参,可复用;
2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
function Student(name,sex,age,stuID){
// 继承构造 call apply
// Person.call(this,name,sex,age);
Person.apply(this,[name,sex,age]);
// 添加新的构造
this.stuID = stuID;
}
// 继承原型
Student.prototype = new Person();
Student.prototype.getID = function(){
console.log(this.stuID);
}
var stu = new Student('Jack','男',18,'10010');
console.log(stu.name);
console.log(stu.stuID);
console.log(stu.ID);
stu.eat();
stu.getID();
console.log(stu);
7. 寄生继承
特点:
1, 融合了以上所有方式的优点
2, 实现复杂
function Student(name,sex,age,stuID){
Person.call(this,name,sex,age);
this.stuID = stuID;
this.study = function(){
console.log('我正在学习');
}
}
(function(){
// 创建一个super类,作为中间媒介,进行传递
var Super = function(){};
// 将该类原型执行person的原型
Super.prototype = Person.prototype;
// 子类的原型指向 super的实例
Student.prototype = new Super();
})()
var stu = new Student('Jack','男',18,'10010');
console.log(stu.ID);
console.log(stu.name);
stu.play()
综合案例:使用面向对象实现选项卡效果
<style>
#div1 div{
width:300px;
height: 100px;
border: 1px solid #f00;
display: none;
}
.active{
background-color: #f00;
color: #fff;
}
</style>
<div id="div1">
<input class="active" type="button" name="" id="" value="歌星">
<input type="button" name="" id="" value='诗人'>
<input type="button" name="" id="" value='作家'>
<div style="display: block;">林俊杰,周杰伦</div>
<div>李白,杜甫</div>
<div>朱自清,鲁迅</div>
</div>
<script>
var oBtn = null;
var aDiv = null;
// TabSwitch 构造函数
function TabSwitch(){
var _this = this; // 对象
var oDiv = document.querySelector(id);
this.oBtn = oDiv.getElementsByTagName('input');
this.aDiv = oDiv.getElementsByTagName('div');
for(var i=0;i<this.oBtn.length;i++){
this.oBtn[i].index = i;
this.oBtn[i].onclick = function(){
_this.fnClick(this);
};
}
}
TabSwitch();
</script>