面向对象
好处:易于分工与合作,适合大型项目开发;健壮性强,易于维护
特征:封装、继承、多态
问题:每创建一个对象,都会在对象中添加一个方法,方法是不变的,若创建每个对象会造成内存浪费。
解决:把方法抽取出来,让所有 实例对象共享。
实现共享:使用原型(把方法封装到原型中)
原型也是一个对象
1. 获取原型的方式:构造函数名.prototype
2. 构造函数与原型的关系
* 构造函数可以通过prtotype找到原型
* 原型可以通过construcror找到构造函数
实现对象在调用一个属性和方法时过程如下:
①会先从实例对象中找
②若自身没有找到则通过__prot__找到原型,去原型中找
③若原型中没有,则通过原型的__proto__找到原型的原型去找
之所以会找到因为原型链的存在
更改子类的原型指向(或给子类的原型重新赋值)
//定义person构造函数
function Person(){
this.name = ‘名字‘;
this.age = 10;
this.gender = ‘男‘;
}
//给构造函数原型添加方法
Person.prototype.sayhi = function(){
console.log(‘hi‘)
}
//实例化对象p1
var p1 = new Person()
//定义student构造函数
function Student(){
this.stuId = ‘10100‘;
}
Student.prorotype = p1;
//实例化学生对象
var stu1 = new Student();
console.log(s1.name); //输出name值是p1.name的值
原型继承的问题和优点:
缺点:继承的属性是没有意义的;
优点:继承的方法是有意义的;
call方法改变this指向
函数体内的this指向是在函数调用时决定的(指向调用者)
语法:函数名.call( 调用者,参数 )
作用:该函数会立即执行,但函数体内的this会指向借用者
function fn(n, a){
this.name = n;
this.age = a;
}
//定义对象
var obj1 = {type: ‘obj‘};
//fn借用给obj1
fn.call( obj1, ‘展示‘, 11)
call方法实现借用
在子类构造函数中借用父类构造函数,并且在 call 中传入子类对象
//per构造函数
function Per(n,a){
this.name = n;
this.age = a;
}
Per.prototype.sayhi = function(){
console.log(this.name + ‘hi‘)
}
//stu构造函数
function Stu(n,a){
this.sid = ‘10001‘
Per.call(this, n, a) //Per谁调用借给谁
}
var s1 = new Stu(‘栈‘, 23)
console.log(s1.name); //输出栈
借用继承的问题和优点
缺点:仅仅借用了属性(原因:call方法在被per类调用执行时仅仅让调用者执行函数内部的程序,函数外的没有借用执行)
优点:很好的继承属性
解决:原型+借用
原型:借用方法 + 借用:继承属性
//创建per构造函数
function per(n, a){
this.name = n;
this.age = a;
}
//为per添加原型
per.prototype.sayhi = function(){
console.log(this.name + ‘hi‘)
}
//创建stu构造函数
function stu(n,a){
this.sid = ‘1001011‘
//借用属性
per.call(this, n, a)
}
//借用方法
stu.prototype = per.prototype
var s1 = new stu(‘组件‘, 22)
console.log(s1.name) //输出组件
s1.sayhi() //输出组件hi
组合继承问题:
缺点:stu类和per指向同一个原型,stu的原型改变时人类的原型也会被改变
优点:让他们不要指向同一个原型,使用各自的原型(对象之间的拷贝)
对象B拷贝对象A的属性和方法
//一个对象拷贝另一个对象中的属性和方法,parentObj父对象 childObj子对象
function copy(parentObj, childObj){
for(var key in parentObj){
if( childObj[kye] == undefined ){
childObj[key] = parentObj[key];
}
}
}
//对象A
var wjl = {
name: ‘大熊‘,
money: 100,
house: ‘大屋子‘,
cars: [‘qq‘, ‘11‘],
sayhi: function(){
console.log(‘大家好,我叫‘+this.name);
}
}
//对象B
var wsc = {
name: ‘小熊‘
}
copy(wjl,wsc)
拷贝方法 + 借用属性
// 【人类】
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.sayHi = function () {
console.log(‘大家好....O(∩_∩)O哈哈~,我叫‘ + this.name);
}
Person.prototype.eat = function () {
console.log(‘我会吃饭.....‘);
};
// 【学生类】
function Student(name, age, gender) {
// this代表谁?代表一个学生的实例对象 s1、s2
this.stuId = ‘10010‘;
// --借用函数--Person[仅仅借用函数体中内容,并没有借用函数体之外其他程序]
Person.call(this, name, age, gender)
};
// [拷贝人类原型(wjl)中的方法给学生原型(wsc)]
copy(Person.prototype, Student.prototype);
Student.prototype.test = function() {
console.log(‘我超爱考试‘);
}
// 【老师类】
function Teacher(name, age, gender) {
// 借用继承-借用人类的属性
Person.call(this, name, age, gender);
}
// [拷贝人类原型(wjl)中的方法给老师原型(wsc)]
copy(Person.prototype, Teacher.prototype);
// 创建学生对象
var s1 = new Student(‘张三‘,19,‘男‘);
// 创建老师对象
var t1 = new Teacher(‘李四‘,39,‘男‘);
普通函数中this指向window
构造函数中this指向当前所创建的对象
定时器中this指向window
对象方法中的this指向调用者
事件处理程序中this指向事件源
语法:函数名.apply( 调用者,[参数......] )
作用:函数被借用时,会立即执行,并且函数体内的this会指向借用者
语法:函数名.apply(调用者, 参数.......)
作用:函数被借用时,不会立即执行,返回新的函数。函数体内this指向调用者
闭包就是能够读取/设置 他函数 内部变量的函数;闭包就是将函数内部和函数外部链接起来的桥梁。
可以在函数外部读取函数函数内部成员;让函数内成员始终活在内存中(延长变量生命周期)
①函数嵌套
②外层函数,必须要有局部变量
③内部函数必须要操作外层函数的局部变量,并且在外层中返回内层函数或内层函数在外部被间接的调用。
调试:在内层函数中设置断点,若进入来在右侧中的scope下可以看到closure,就表示产生了闭包。 在实际开发中,不要可以使用闭包,因为延长生命周期的变量可能会一直存活在内存中。 解决方案:把外层变量设置为null
作用:减少代码量
缺点:若递归的层数比较多时,会消耗CPU。浪费内存,影响性能。
若将来递归层较少时,可以选择用递归方式。
原文:https://www.cnblogs.com/xhrr/p/11172927.html