每个对象都有一个特殊的属性叫做原型,原型分为隐式原型(__ proto __)和显示原型(prototype),对象的隐式原型指向他构造函数的显示原型。
原型链原理:从一个实例对象往上找构造这个实例的相关联的对象,这个相关联的对象往上找又有创造他的上一级的原型对象,以此类推,一直到object.prototype终止。
总结:一切对象都是继承自Object对象,Object对象直接继承自根源对象 NULL,一切函数对象(包括Object对象),都是继承自Function对象,Function对象的 __ proto __会指向自己的原型对象,最终还是继承 Object对象。
有权访问另一个函数作用域中的变量的函数,创建闭包的方式,在一个函数的内部创建一个函数。
闭包优点:
闭包缺点:
两等会尝试帮你进行一次类型转换再做相等操作,除非判断是否为 NULL 或 Undefined,一律使用三等。
提到事件循环,首先要知道JS是如何执行的:
Event Loop的执行过程:
总结:只有一个线程来执行JS函数,而微任务的监听及触发交给JS引擎的其他线程执行,宏任务的监听和触发交给浏览器的线程来执行,中间插入渲染引擎进行DOM渲染,这样不断轮询就构成了Event Loop
删除:
添加:
splice:
继承的几种方法
1、 借助构造函数实现继承(原理是在子类中改变父级this的指向,缺点是只能部分继承,不能继承父类原型对象上的方法);
function Parent_1 () {
this.name = 'Parent_1'
}
Parent_1.prototype.sayHi = function () {}
function Child_1 () {
Parent_1.call(this) // Parent_1.apply(this)
}
2、 借助原型链继承(缺点是原型链上的对象是共用的,改变某个对象的属性,原型链上的其他对象属性也会改变);
function Parent_2 () {
this.name = 'Parent2'
this.play = [1, 2, 3]
}
function Child_2 () {
this.type = 'child2'
}
Child_2.prototype = new Parent_2()
var s1 = new Child_2();
var s2 = new Child_2();
console.log(s1.play, s2.play);
s1.play.push(4);
3、 组合方式(缺点是父级的构造函数执行了两次并把父类的constructor也继承了);
function Parent_3 () {
this.name = 'Parent3'
this.play = [1, 2, 3]
}
function Child_3 () {
Parent_3.call(this)
this.type = 'child3'
}
Child_3.prototype = new Parent_3()
var s3 = new Child_3();
var s4 = new Child_3();
console.log(s3.play, s4.play);
4、组合继承优化1(缺点是把父类的constructor也继承了);
function Parent_4 () {
this.name = 'Parent4'
this.play = [1, 2, 3]
}
function Child_4 () {
this.type = 'child4'
}
Child_4.prototype = Parent_4.prototype
var s5 = new Child_4();
var s6 = new Child_4();
console.log(s5, s6);
console.log(s5 instanseof child_4, s5 instanceof Parent_4)
5、组合继承优化2(原理是通过Object.create方法创建一个中间对象,参数是该对象的原型对象,然后把子类的构造函数赋值为该子类)。
function Parent_5 () {
this.name = 'Parent5'
this.play = [1, 2, 3]
}
function Child_5 () {
Parent_5.call(this)
this.type = 'child5'
}
Child_5.prototype = Object.create(Parent_5.prototype)
6、通过ES6的class来创建,使用extends关键词来继承
class Person {
constructor(name) {
this.name = name
}
sayHi () {
return `i'm ${this.name}`
}
}
class Student extends Person {
constructor(name, age, sex) {
super(name) // 通过super关键词调用父类的属性
this.age = age
this.sex = sex
}
... // 下面写自己的方法
}
Array.isArray(‘变量’)
// 返回的是布尔值,true是数组,否则不是
var arr = [1,2,3,1];
alert(arr instanceof Array); // true
function isArrayFn (o) {
return Object.prototype.toString.call(o) === '[object Array]';
}
var arr = [1,2,3,1];
alert(isArrayFn(arr));// true
var arr = [1,2,3,1];
alert(arr.constructor === Array); // true
Array.prototype.myMap = function (fn, ctx) {
let oArray = Array.prototype.slice.call(this)
let newArr = []
let length = oArray.length
for (let i = 0; i < length; i++) {
if (!oArray.hasOwnProperty(i)) {
newArr.length++
} else {
newArr.push(fn.call(ctx, oArray[i], i, this))
}
}
return newArr
}
1、箭头函数的 this 为父作用域的 this ,并不是调用的。
箭头函数的this永远指向其父作用域,任何方法都改变不了,包括call,apply,bind。
普通函数的this指向调用它的那个对象。
let person = {
name:'jike',
init:function(){
//为body添加一个点击事件,看看这个点击后的this属性有什么不同
document.body.onclick = ()=>{
alert(this.name);//?? this在浏览器默认是调用时的对象,可变的?
}
}
}
person.init();
上例中的 init
是 function,以 person.init()
调用,其内部的onclick
是箭头函数,所以 this
指向父作用域的变量,也就是 person 里的name
let person = {
name:'jike',
init: () => {
//为body添加一个点击事件,看看这个点击后的this属性有什么不同
document.body.onclick = ()=>{
alert(this.name);//?? this在浏览器默认是调用时的对象,可变的?
}
}
}
person.init();
上例的 init
函数是以箭头函数声明的,其内部的 this
为全局windows对象,onclick
的this
,也就是init
函数的this
,也是window
,得到的this.name
就为undefined
。
2、箭头函数不能作为构造函数
function Person (name) {
this.name = name
}
const Person = (name) => {
this.name = name
}
由于 this
必须是对象实例,而箭头函数是没有实例的,此处的 this
又指向别处,不能产生person实例,自相矛盾。
3、箭头函数没有 arguments、caller、callee 箭头函数本身没有 arguments,如果箭头函数在一个 function 里面,它会将外部的 arguments 拿过来使用,箭头函数中想要接受不定参数,可以使用 rest参数...(扩展运算符)来解决
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
4、箭头函数通过call或apply调用,无法改变this指向,只会传入参数
let obj2 = {
a: 10,
b: function(n) {
let f = (n) => n + this.a;
return f(n);
},
c: function(n) {
let f = (n) => n + this.a;
let m = {
a: 20
};
return f.call(m,n);
}
};
console.log(obj2.b(1)); // 11
console.log(obj2.c(1)); // 11
5、箭头函数没有原型属性
var a = () => {
return 1;
}
function b () {
return 2;
}
console.log(a.prototype); // undefined
console.log(b.prototype); // {constructor: ƒ}
6、箭头函数在 ES6 class 中声明的方法为实例方法,不是原型方法
//deom1
class Super{
sayName(){
//do some thing here
}
}
//通过Super.prototype可以访问到sayName方法,这种形式定义的方法,都是定义在prototype上
var a = new Super()
var b = new Super()
a.sayName === b.sayName //true
//所有实例化之后的对象共享prototypy上的sayName方法
//demo2
class Super{
sayName =()=>{
//do some thing here
}
}
//通过Super.prototype访问不到sayName方法,该方法没有定义在prototype上
var a = new Super()
var b = new Super()
a.sayName === b.sayName //false
//实例化之后的对象各自拥有自己的sayName方法,比demo1需要更多的内存空间