原型拓展方法+继承
扩展原型上的方法
数组indexOf的扩展
// console.log(Array.prototype.indexOf);
// 在标准浏览器返回函数,在IE8及下返回undefined
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (val, index) {
index = typeof index === 'undefined' ? 0 : index;
for (var i = index; i < this.length; i++) {
if (this[i] === val) {
return i;
}
}
return -1;
}
}
var arr = [3, 4, 2, 3, 3, 4];
console.log(arr.indexOf(3)); // 0
console.log(arr.indexOf(3, 1)); // 3
字符串trim的扩展
// 字符串的trim
// 在标准浏览器下返回函数,在IE8及以下返回undefined
// console.log(String.prototype.trim);
// 扩展IE8及以下原型上的trim
if (!String.prototype.trim) {
String.prototype.trim = function () {
var re = /^\s+|\s+$/g;
return this.replace(re, '');
}
}
var str = ' 平头哥 ';
console.log('(' + str.trim() + ')');
call与apply与bind
- 函数.call(新的this指向, 参数1, 参数2, …)
- 函数.apply(新的this指向, [参数1, 参数2, …])
- 函数.bind(新的this指向, 参数1, 参数2, …)
作用:call与apply都是调用函数,而bind不会调用函数,都修改函数中的this的指向。
区别:call与bind的后续参数是以一个参数列表传入,而apply的后续参数是以一个数组传入
共同点:修改函数中的this,第一个参数都是新的this指向,都有返回值,call与apply返回值就是函数返回值,而bind返回值为由指定的 this 值和初始化参数改造的原函数拷贝,因为bind不会调用函数。
function fn(a, b) {
console.log(this);
console.log(a, b);
}
fn(3, 5);
fn.call(document, 5, 6);
fn.apply(document, [7, 8]);
var f = fn.bind(document,5,6);
f();
// --------------------------------
// 如果第一个参数是null 和 undefined,则this指向window
fn.call(undefined, 5, 6);
// 找出数组的最大值
var arr = [4, 6, 2, 6, 5, 8, 4, 7, 3];
// Math.max(4, 6, 2, 6, 5, 8, 4, 7, 3)
console.log(Math.max.apply(Math, arr));
继承
原型链继承
-
原型链继承:将父的实例赋给子的原型
-
如何用一句话实现继承:将父的实例赋给子的原型。
-
不足:
1、如果父级中有引用类型,创建的子级会一改全改
2、constructor本来应该指向自己的构造函数,但是却指向了父级的构造函数
// 父类(学生类)
function Student() {
this.job = '学生';
this.ah = ['打游戏', '睡觉', '找平头妹'];
}
Student.prototype.showJob = function () {
console.log(this.job);
}
// ----------------------------------------
// 子类(小学生)
function SmallStudent(name) {
this.name = name; // 子自己添加的
}
SmallStudent.prototype = new Student(); // 继承了父的属性和方法
// 原型链继承:将父的实例赋给子的原型
// 如何用一句话实现继承:原型链继承
// 不足:
// 1、如果父级中有引用类型,创建的子级会一改全改属性
// 2、constructor本来应该指向自己的构造函数,但是却指向了父级的构造函数
//解决问题1、手动改变constructor指向
SmallStrudent.prototype.construcor = SmallStrudent;
// -------------------------------------
var s1 = new SmallStudent('zs');
console.log(s1);
console.log(s1.name);
console.log(s1.job);
console.log(s1.ah);
s1.showJob();
console.log(s1.constructor); // Student,本来应该指向SmallStudent
s1.ah.push('画画'); // 修改s1的爱好,后面创建的s2也会改变
var s2 = new SmallStudent('ls');
console.log(s2);
console.log(s2.ah); // ["打游戏", "睡觉", "找平头妹", "画画"]
对象冒充(call)继承
- 对象冒充继承:在子的构造函数中,调用父类的构造函数,并用call改this指向
- 不足:不能继承原型上的方法。
// 父类(学生类)
function Student() {
this.job = '学生';
this.ah = ['打游戏', '睡觉', '找平头妹'];
}
Student.prototype.showJob = function () {
console.log(this.job);
}
// ----------------------------------------
// 子类(小学生)
function SmallStudent(name) {
Student.call(this); // 继承了父类构造函数中的属性
this.name = name; // 加上我自己的属性
}
// 对象冒充继承:在子类的构造函数中,调父类的构造函数,并用call改this指向
// 好处:解决了原型链继承引用类型一改全改属性的问题
// 问题:只是继承了父类的属性,而没有继承父类原型上的方法
// -----------------------------------
var s1 = new SmallStudent('zs');
s1.ah.push('画画');
console.log(s1);
var s2 = new SmallStudent('ls');
console.log(s2);
组合继承(已能实现但浪费性能)
- 组合继承:对象冒充+原型链
- 对象冒充继承属性
- 原型链继承方法
// 父类(学生类)
function Student() {
this.job = '学生';
this.ah = ['打游戏', '睡觉', '找平头妹'];
}
Student.prototype.showJob = function () {
console.log(this.job);
}
// ----------------------------------------
// 子类(小学生)
function SmallStudent(name) {
Student.call(this); // 对象冒充继承
this.name = name;
}
SmallStudent.prototype = new Student(); // 原型链继承
// 组合继承:原型链继承 + 对象冒充继承
// 问题
// 1、constructor指向不对
// 2、同名的属性会在原型链中出现两次
// 3、父的构造函数会被调用两次
//解决问题1、手动改变constructor指向
SmallStrudent.prototype.construcor = SmallStrudent;
// -------------------------------------------
var s1 = new SmallStudent('zs');
console.log(s1);
原型链继承图:
寄生组合继承 (继承的标准模式)
// 父类(学生类)
function Student() {
this.job = '学生';
this.ah = ['打游戏', '睡觉', '找平头妹'];
}
Student.prototype.showJob = function () {
console.log(this.job);
}
// ----------------------------------------
// 子类(小学生)
function SmallStudent(name) {
Student.call(this); // 继承父的属性
this.name = name; // 添加子类自己的属性(写在继承的后面)
}
// 继承父的原型上的方法
inherits(SmallStudent, Student);
// 添加自己原型上的方法,一定要写在继承的后面
SmallStudent.prototype.showName = function () {
console.log(this.name);
}
// 寄生组合式继承(继承的标准模式)
// 父的属性和方法要分开继承
// -----------------------------------------
var s1 = new SmallStudent('zs');
console.log(s1);
// console.log(s1.constructor);
// ------------------------------------------
// 寄生组合继承父类原型上方法的封装,原型链继承升级
function inherits(Child, Parent) {
var F = function () { };
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TqT3xLvi-1632922046848)(C:\Users\Administrator\Desktop\课堂笔记\第二阶段\笔记\image\对象冒充继承.jpg)]
原型链继承升级图:
原型对象引用总结(地址问题)
- 上图箭头指向谁即谁的地址被引用,如Fun实例.__proto__指向Fun原型,即Fun实例拿到Fun原型的地址。
- Son.prototype = new Fun(); 表示Son.prototype拿到Fun实例对象的地址,这时采用赋值形式则Son.prototype改变内容则Fun里的内容也会改变,因为地址一样。
- 而Fun实例.__proto__指向Fun原型,Fun实例拿到Fun原型的地址,则改变Fun实例不会改变Fun原型,因为他们之间用__proto__指向的。如改变Fun实例.__proto__则会改变Fun原型。同理:原型.constructor一样。
- Fun.prototype = Farther.prototype; 表示
继承应用
判断所有数据类型
var obj = {};
console.log(obj);
//this指向实例对象obj。所以打印[object Object]。
console.log(obj.toString());
//本来直接调用this指向Object原型对象上。所以打印[object Object]。
console.log(Object.prototype.toString());
//改变toString的this指向。使this指向实例对象上。而这个实例对象就是数据类型是由不同构造函数构造的。打印[object 构造函数]。
console.log(Object.prototype.toString.call());
//例:1为Number构造函数的实例对象,所以打印[object Number]。
console.log(Object.prototype.toString.call(1));
面象对象注意事项
实例对象方法的引用方式
function Person() {
this.uname = '学生';
this.age = 1;
this.fn2();//第二种方式。
}
Person.prototype.fn1 = function () {
console.log(1);
}
Person.prototype.fn2 = function () {
console.log(2);
}
Person.prototype.fn3 = function () {
console.log(3);
}
var s1 = new Person();
s1.fn1();//第一种方式。
new.Person().fn3();//第三种方式。
this的指向问题
this指向为谁调用指向谁,主要看在谁的作用域里。拥有这个作用域的函数被谁调用就指向谁。
- 谁调用了函数,this就指向谁
- this不是在定义的时候确定的,而是在调用的时候确定的
// 1、全局环境下的this: window
console.log(this);
// 2、事件处理函数中的this:是触发这个事件的元素
box.onclick = function () {
console.log(this); // box
}
// 3、定时器中的this: window
setTimeout(function () {
console.log(this);
}, 1000);
// 4、函数直接执行中的this, 非严格模式是window,严格模式下是undefined
//"use strict"; // 严格模式
function fn() {
console.log(this);
}
fn();
// 5、call apply
// 6、对象方法内部调用
var obj = {
fn: function () {
console.log(this);
}
};
obj.fn(); // obj
var v = obj.fn;
v(); // window
// 7、构造函数中的this:new 出来的哪个对象(实例)
new将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)。
// 8、原型链函数中的this,一般都指实例
因为只能由实例调用使用。
// 9、所有的回调函数里面的this是 window
move(box, {}, function () { })
这个回调函数是实参在全局作用域里。
法内部调用
var obj = {
fn: function () {
console.log(this);
}
};
obj.fn(); // obj
var v = obj.fn;
v(); // window
// 7、构造函数中的this:new 出来的哪个对象(实例)
new将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)。
// 8、原型链函数中的this,一般都指实例
因为只能由实例调用使用。
// 9、所有的回调函数里面的this是 window
move(box, {}, function () { })
这个回调函数是实参在全局作用域里。