原理: 使用父类的示例重写子类的原型。
function SuperType(){
this.prototype = true
}
SuperType.prototype.getSuperValue = function(){
return this.prototype
}
function SubType(){
this.subproperty = false
}
//继承
SuperType.prototype = new SuperType()
SuperType.prototype.getSuperValue = function(){
return this.subproperty()
}
var instance = new SubType()
alert(instance.getSubValue) //true
缺点:
原理: 在子类构造函数的内部调用父类的构造函数,将子类执行环境this用call方法传到父类的构造函数中,使父类中的属性和方法被重写到子类上。
function SuperType(){
this.colors = ["red", "green", "blue"]
}
function SubType(){ //继承了SuperType
SuperType.call(this)
}
var instance1 = new SubType()
instance1.color.push("black")
alert(instance1.colors) //"red, green, blue, black"
var instance2 = new SubType()
alert(instance2.colors) //"red, green, blue"
缺点:
优点:
原理: 使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承
function SuperType(name){
this.name = name
this.color = ["red", "blue"]
}
SuperType.prototype.sayName = function(){
alert(this.name)
}
function SubType(name, age){ //继承属性
SuperType.call(this.age)
this.age = age
}
//继承方法
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function(){
alert(this.age)
}
var instance1 = new SubType("Gracy" , 29)
instance1.colors.push("black")
alert(instance1.colors) //"red,blue,black"
instance1.sayName() //"Gracy"
instance1.sayAge() //29
var instacne2 = new SubType("Jam", 25)
alert(instance2.colors) //"red,blue"
instance1.sayName() //"Jam"
instance2.sayAge() //25
优点:
原理: 通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。就是使用寄生式继承父类的原型,然后再将结果指定给子类的原型。
//原型式继承,相当于 Object.create()
function object(o){
function F(){}
F.prototype = o;
return new F();
}
//寄生式继承
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
//寄生组合式继承
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
优点:
在es6 中,官方给出了 class 关键字来实现面向对象风格的写法,但本质上是寄生组合式继承的语法糖。
class Person(){
constructor(age){
this.age_ = age
}
sayAge(){
console.log(this.age_)
}
//静态方法
static create(){
return new Person(Math.floor(Math.random()*100))
}
}
//继承普通构造函数
class Doctor extends Person{}
const doctor = new Doctor(32)
doctor.sayAge() //32
1)原型是什么?
给其他对象提供共享属性的对象,简称为原型。
所有对象,都有一个隐式引用,它被称之为这个对象的prototype原型。当访问对象的某个属性时,会先在对象本身的属性上寻找,没找到就会去它的原型对象上寻找。
__proto__ & constructor 概念简介:
__proto__属性来访问对象的原型2)原型的作用
作用: 共享属性
如果在一个对象上找不到某个属性,就会去它的原型对象上找,以此类推直至找到,或者寻找到原型链的终点都没找到则不存在这个属性;这个特性让任意创建的对象都拥有通用的各种方法,例如:toString、hasOwnProperty、isPrototypeOf等
3)原型的应用
Object.prototype上的方法const obj = {};
obj.toString();
function Person(){}
Person.prototype.name = 'tom'
Person.prototype.sayName = function(){
console.log(this.name)
}
const person1 = new Person()
person1.sayName() //"tom"
function SuperType(){
this.name = 'tom'
}
SuperType.prototype.sayName = function(){
alert(this.name)
}
function SubType(){}
//继承了SuperType()
SubType.prototype = new SuperType()
const instance = new SubType()
instance.sayName() // 'tom'
4)优缺点
优点
缺点
什么是原型链: 如果要访问对象中并不存在的一个属性,就会查找内部prototype关联的
对象。在查找属性时对它进行层层寻找遍历,这个关联关系实际上定义了一条原型链。
原型链最终会指向Object.prototype,原型链的终点是Object.prototype.__proto__ 也就是null
原型属性判断&设置
1)getPrototypeOf
Object.getPrototypeOf(obj)间接访问指定对象的prototype对象。
2)setPrototypeOf
Object.setPrototypeOf(obj, obj1) 间接设置指定对象的prototype对象。
3)hasOwnProperty
是否仅属于对象本身的属性。
枚举方法
1)getOwnPropertyNames
返回所有实例本身的属性。
2)in
只要通过对象可以访问,in操作符就返回true,也就是说在对象或者原型链上存在,就会返回true
3)for…in
可以通过对象访问,且可以枚举出来的属性都会被返回。包括实例属性和原型属性。
4)Object.keys
对象上所有可枚举的实例本身属性。
5) Object.values
返回对象值的数组 版本:es8+
6)Object.entries
返回键/值对的数组 版本:es8+
定义: 闭包是指有权访问另一个函数作用域中的变量的函数。
变量的作用域,就是指变量的有效范围。随着代码执行环境创建的作用域链往外层逐层搜索,一直搜索到全局对象为止。
由于IE的js对象和dom对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁在内存中的dom元素。
因此我们在不需要使用dom时,应将dom解除引用来避免内存泄露:
function closure(){
// div用完之后一直存在内存中,无法被回收
var div = document.getElementById('div');
div.onclick = function () {
console.log(div.innerHTML);// 这里用oDiv导致内存泄露
};
}
// 解除引用避免内存泄露
function closure(){
var div = document.getElementById('div');
var test = div.innerHTML;
div.onclick = function () {
console.log(test);
};
div = null;
}
闭包可以使代码组织方式的自由度大大提升,在日常使用中有非常广泛的用途。
简单的有:
举例一些应用场景:
1)构造函数的私有属性:由于js没有类的实现,某些不希望被外部修改的私有属性可以通过闭包的方式实现。
function Person(param) {
var name = param.name; // 私有属性
this.age = 18; // 共有属性
this.sayName = function () {
console.log(name);
}
}
const tom = new Person({name: 'tom'});
tom.age += 1; // 共有属性,外部可以更改
tom.sayName(); // tom
tom.name = 'jerry';// 共有属性,外部不可更改
tom.sayName(); // tom
2)计算缓存
// 平方计算
var square = (function () {
var cache = {};
return function(n) {
if (!cache[n]) {
cache[n] = n * n;
}
return cache[n];
}
})();
3)函数节流、防抖
// 节流
function throttle(fn, delay) {
var timer = null, firstTime = true;
return function () {
if (timer) { return false;}
var that = this;
var args = arguments;
fn.apply(that, args);
timer = setTimeout(function () {
clearTimeout(timer);
timer = null;
}, delay || 500);
};
}
// 防抖
function debounce(fn, delay) {
var timer = null;
return function () {
var that = this;
var args = arguments;
clearTimeout(timer);// 清除重新计时
timer = setTimeout(function () {
fn.apply(that, args);
}, delay || 500);
};
}
只在开始执行一次,未执行完成过程中触发的可忽略,核心在于关闭锁。
举个例子:多次点击按钮提交表单,第一次有效,按钮防重
// 节流
function throttle(fn, delay) {
var timer = null;
return function () {
if (timer) { return false;}
var that = this;
var args = arguments;
fn.apply(that, args);
timer = setTimeout(function () {
clearTimeout(timer);
timer = null;
}, delay || 500);
};
}
// 使用
function clickHandler() {
console.log('节流click!');
}
const handler = throttle(clickHandler);
document.getElementById('button').addEventListener('click', handler);
只执行最后一次被触发的,清除之前的异步任务,核心在于清零。
举个例子:页面滚动处理事件,搜索框输入联想,最后一次有效
// 防抖
function debounce(fn, delay) {
var timer = null;
return function () {
var that = this;
var args = arguments;
clearTimeout(timer);// 清除重新计时
timer = setTimeout(function () {
fn.apply(that, args);
}, delay || 500);
};
}
// 使用
function clickHandler() {
console.log('防抖click!');
}
const handler = debounce(clickHandler);
document.getElementById('button').addEventListener('click', handler);
⚠️关于容易搞混的问题,就记住:节流第一次有效,防抖反之
this总是指向执行时的当前对象。
js的this总是指向一个对象,而具体指向哪个对象是运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
也就是说this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
场景的几种情况:
Function.prototype.call 或 Function.prototype.apply 调用。1)作为对象的方法调用
对象的方法被调用时,this指向该对象。
var obj ={
a:1,
getA(){
alert(this==obj)
}
}
obj.getA()
2)作为普通函数的调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的this总是指向全局对象。
//在浏览器的JavaScript 里,这个全局对象是window 对象。
window.name = 'globalName';
var getName = function(){
return this.name;
};
console.log( getName() ); // 输出:globalName
3)构造器调用
通常情况下,构造器里的this就是指向返回的这个对象
//构造器里的this 就指向返回的这个对象,见如下代码:
var MyClass = function(){
this.name = 'sven';
};
var obj = new MyClass();
alert ( obj.name ); // 输出:sven
var MyClass = function(){
this.name = 'sven';
return { // 显式地返回一个对象
name: 'anne'
}
};
var obj = new MyClass();
alert ( obj.name ); // 输出:anne
var MyClass = function(){
this.name = 'sven'
return 'anne'; // 返回string 类型
};
var obj = new MyClass();
alert ( obj.name ); // 输出:sven
4)call 或 apply 调用
apply和call可以动态地改变传入函数的 this
var obj1 = {
name: 'sven',
getName: function(){
return this.name;
}
};
var obj2 = {
name: 'anne'
};
console.log( obj1.getName() ); // 输出: sven
console.log( obj1.getName.call( obj2 ) ); // 输出:anne
1)简介和区别
call和apply的作用一模一样,区别仅在于传入参数形式的不同。
apply
apply接收两个参数,第一个参数指定了函数体内this对象的指向,第二个参数为一个带下标的集合,可以为数组,也可以为类数组(对象本身可以存储属性且对象的length属性可以读写)
call
call传入的参数数量不固定,第一个参数也是代表函数体内的this指向,从第二个参数往后,每个参数被依次传入函数。
bind
bind参数类型和call相同,不过它不会执行函数,而是修改this后返回一个新的函数
ar func = function( a, b, c ){
alert ( [ a, b, c ] );
};
func.apply( null, [ 1, 2, 3 ] );
func.call( null, 1, 2, 3 );
手动实现apply
Function.prototype.apply = function(context){
var context = context || window
var args = arguments[1] || []
context.fn = this
var result = context.fn(...args)
delete context.fn
return result
}
call和bind是包装在apply上面的语法糖
Function.prototype.call = function( context ){
var argus = [];
for (var i = 1; i < arguments.length; i++) {
argus.push(arguments[i]);
}
this.apply(context, argus);
};
Function.prototype.bind = function( context ){
var self = this; // 保存原函数
// 返回一个新的函数
return function(){
// 执行新的函数的时候,会把之前传入的 context // 当作新函数体内的 this
return self.apply( context, arguments );
}
};
2)用途
Array.prototype 对象借用方法Array.prototype.push.call(a, 'first' )promise有两大特点:
优点:
缺点:
// 私有变量,三个状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
/**
* 自己实现Promise
*/
class MyPromise {
constructor(calllback) {
// 状态
this.status = PENDING;
// 值
this.value = undefined;
// 成功回调列表
this.resolveList = [];
// 失败回调列表
this.rejectList = [];
const resolve = (value) => {
// 状态仅允许修改一次
if (this.status !== PENDING){
return;
}
setTimeout(() => {
this.value = value;
this.status = FULFILLED;
for (const func of this.resolveList) {
func(this.value);
}
}, 0);
}
const reject = (reason) => {
// 状态仅允许修改一次
if (this.status !== PENDING){
return;
}
setTimeout(() => {
this.value = reason;
this.status = REJECTED;
for (const func of this.rejectList) {
func(this.value);
}
}, 0);
}
try {
calllback(resolve, reject);
} catch (err) {
reject(err);
}
}
// 处理执行结果
then(onResolve, onReject) {
let promise = null;
if (this.status === PENDING) {
promise = new MyPromise((resolve, reject) => {
// 解决值穿透
onResolve = typeof onResolve === 'function' ? onResolve : (value) => value;
onReject = typeof onReject === 'function' ? onReject : (reason) => {throw reason};
this.resolveList.push(function(innerValue) {
try {
const value = onResolve(innerValue);
// resolve(value);
runThen(promise, value, resolve, reject);
} catch (err) {
reject(err);
}
});
this.rejectList.push(function(innerReason) {
try {
const value = onReject(innerReason);
// resolve(value);
runThen(promise, value, resolve, reject);
} catch (err) {
reject(err);
}
});
});
} else {
const innerValue = this.value;
const isFulfilled = this.status === STATUS.FULFILLED;
promise = new MyPromise((resolve, reject) => {
try {
const value = isFulfilled ? onResolve(innerValue) : onReject(innerValue); // 失败状态调用 onReject
// resolve(value);
runThen(promise, value, resolve, reject);
} catch (error) {
reject(error)
}
})
}
return promise;
// 运行promise,处理then返回新promise和循环引用问题
function runThen(promise, value, resolve, reject) {
if (promise === value) {
reject(new TypeError('Chaining cycle detected for promise'));
return;
}
if (value instanceof MyPromise) {
value.then((val) => runThen(promise, val, resolve, reject), (reason) => reject(reason));
} else {
resolve(value);
}
}
}
// 处理失败
catch(onReject) {
return this.then(null, onReject);
}
}