作用域:
1、对于作用域链我们直接通过创建态来定位作用域链
2、手动取消全局,使用块级作用域
if(true){
let e = 111;
console.log(e);
}
this 上下文:
是执行时动态读取上下文决定的,而不是创建时
this指向
1、函数直接调用中,this指向的是window(包含函数表达式,匿名函数,嵌套函数,setTimeout)
2、隐式绑定 this的指向是调用堆栈的上一级
1)在执行函数时,函数被上一级调用,上下文指向上一级
2)或直接成公共函数,则指向window
3、显示绑定 call/apply/bind 改变this指向
4、dom监听函数中指的是dom元素实例
5、箭头函数
。。。。
**this指向的是new之后得到的实例**
如何突破作用域限制->闭包
闭包:能够读取其他函数中变量的函数;(一个函数和他周围状态的引用捆绑在一起的组合)
1、函数作为返回值
2、函数作为参数
3、函数嵌套
#### 拆分执行
function createIncrement() {
let count = 0;
function increment() {
count++;
}
let message = `count is ${count}`;
function log() {
console.log(message);
}
return [increment, log];
}
const [increment, log] = createIncrement();// 解构
increment();
increment();
increment();
log();
#### 实现私有变量
function createStack() {
const items = [];
return {
push(item) {
items.push(item);
}
}
}
new原理(构建原型链)
1.创建一个对象,并返回对象实例
2.将返回的对象的原型指向构造函数的prototype对象
3.将当前对象实例赋给this
4.执行构造函数初始化,假如构造函数执行返回对象则返回这个对象,否则返回对象实例
function myNew(fnName){
var obj = {};
obj.__proto__ = fnName.prototype;
var result = fnName.call(obj);
return typeof result === 'object' ? result: obj;
}
//call/apply 函数实现原理
Function.prototype.myCall(context){
var context = context || window;
context.fn = this;
var result = context.fn(context.splice(0,1));
delete context.fn;
return result
}
//bind 函数实现原理
Function.prototype.myBind(context){
var _this = this;
return function(){
return _this.mycall(context);
}
}
原型链图:
OOP面向对象
对象是对于单个物体的抽象,是一个容器,封装了属性(对象的状态)和方法(对象的行为);
类是对象模版即表征了一类物体的共同特征,从而生成对象;
js其实本质上并不是基于类,而是基于构造函数 + 原型链(constructor + prototype)
eg:
function Course(teacher){
this.teacher = theacher;
this.leader = 'bubu';
this.startCourse = function(name){
return `上课-${name}`;
}
}
Course本质就是构造函数(调用执行,或new 执行)不初始化无法使用
1、函数体内使用的this,指向所要生成的实例
2、生成对象用new来进行实例化
3、可以做初始化传参
function Course(theacher){ //防止call/bind/apply 强改this
let _isClass = this instanceof Course;
if(!_isClass){
return new Course();
}
this.teacher = theacher;
this.leader = 'bubu';
this.startCourse = function(name){
return `上课-${name}`;
}
}
const course = new Course('yy');
启发:如果编写底层的api代码时,尽量做到不需要让外部感知内部类型
构造函数:用来初始化创建对象的函数-Course
**会自动给构造函数赋予一个属性prototype,该属性等于实例对象的原型对象;
**不足:构造函数中的方法是会存在在每个实例中,重复挂载会导致资源浪费。
原型对象 Course.prototype
实例对象:course是实例对象,根据原型对象创建出来的实例
**每个实例对象中都有一个__proto__
**每个实例对象中都有一个constructor
constructor:每个对象在创建时,会自动拥有一个构造函数属性constructor,其继承自原型对象,是指向了构造函数的引用。
继承:
原型链继承:
function Parent(){
this.name ='name';
this.age = 10;
this.eat= function(){
console.log(`${this.name} eat 10 mantou`);
}
}
function Child(){
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
// 缺点: 无法传参
构造函数继承:
function Parent(name){
this.name =name;
this.age = 10;
this.eat= function(){
console.log(`${this.name} eat 10 mantou`);
}
}
function Child(name){
Parent.call(this, name);
}
const c = new Child();
c.eat();
// 缺点:方法会在每一个实例中创建一份,资源浪费
组合继承:
function Parent(name){
this.name =name;
this.age = 10;
}
Parent.prototype.eat= function(){
console.log(`${this.name} eat 10 mantou`);
}
function Child(name){
Parent.call(this, name);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
// 初始化了两遍父类函数,资源浪费
寄生组合继承:
function Parent(name){
this.name =name;
this.age = 10;
}
Parent.prototype.eat= function(){
console.log(`${this.name} eat 10 mantou`);
}
function Child(name){
Parent.call(this, name);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
多继承:
function Parent(name){
this.name =name;
this.age = 10;
}
Parent.prototype.eat= function(){
console.log(`${this.name} eat 10 mantou`);
}
function Parent2(name){
this.shop =name;
}
Parent2.prototype.speaking= function(){
console.log(`${this.name} eat 10 mantou`);
}
function Child(arg){
Parent.call(this, arg);
Parent2.call(this, arg);
}
Child.prototype = Object.create(Parent.prototype);
Object.assign(Child.prototype, Parent2.prototype);
Child.prototype.constructor = Child;
const c = new Child();
c.eat();
c.speaking();
es6:
1)const/let
1、不允许重复声明,const不允许重复赋值
2、无变量提升
3、块级作用域
4、dead zone 暂时性死区(在声明前使用)
5、引用型const,变量型let
2)class
1、class的类型是function
2、class的prototype 有区分但本质相同
3、class&函数对象 属性
4、course.hasOwnProperty('teacher');
5、set/get
6、extends / super
7、静态方法 Course.ring
8、私有属性三种方式
1)construtor中变量名与参数名不同 _teacher
2)在constructor外部建立变量
class Course {
// init 实例会默认执行
constructor(teacher, course) {
this.teacher = teacher;
this.course = course;
}
// 拓展方法
getCourse() {
return `teacher is:${this.teacher}, course: ${this.course}`;
}
get teacher() {
// 留有空间
return this.teacher;
}
set teacher(val) {
// 留有空间
this.teacher = val;
}
}
3)箭头函数
1、 箭头函数并不会形成独立上下文,内部this指向了window或箭头函数的上一级
2、箭头函数无法成为完整构造类
3、无法构造原型对象的方法
4、无法使用arguments参数
4)解构
1、形参解构
2、变量初始值
3、json
4、ajax返回值
5、函数返回值
6、变量交换 [b, a] = [a, b];
const zhaowa = {
teacher: {
name: '',
age: 30
},
leader: '',
name: 'es6'
}
// 别名
const {
teacher: {
name,
age
},
leader,
name: className
} = zhaowa;
函数式编程
/**
* 函数式编程
* 1.vue3 react16.8以上 全面化函数式推动
* 2.函数式编程可以使得代码单元相对更加独立 - tree shaking过程更顺畅,更方便做UT
* 3.减少了对this的依赖,减轻了开放人员对指向问题的困惑
* 4. js天生友好函数式: ramda、loadsh
* 概念:
* 1.一种抽象运算过程
* 2.函数式的函数并非对于过程运算,函数的映射
* 3.幂等- 相同的输入始终得到相同的输出
* 纯函数
*/
let arr = [1, 2, 3, 4, 5];
arr.slice(0, 3);// 1, 2, 3
arr.slice(0, 3); // 1, 2, 3
arr.splice(0, 3); // 1, 2, 3
arr.splice(0, 3); // 4, 5
/**
* 对于系统的改造
*/
// 不纯函数(依赖外部变量)=》换了环境就不对了, 面向过程
let min = 18;
let limit = age =>age > min;
// 纯纯的
let limit2 = age => age > 18;
/**
* 对于大型系统,对于外部状态的依赖,会大大的提高系统的复杂性
* 问题:
* 18被硬编码到了函数内部,造成了功能拓展的局限
* 高阶函数HOC
* 定义:
* 1.函数作为参数被传递到了另一个函数中,
* 2.函数作为返回值被另一个函数返回
*/
let fn = arg =>{
let outer = "outer";
let innerFn = ()=>{
console.log(outer);
console.log(arg);
}
return innerFn;
}
let closure = fn(18);
// 闭包
/**
* 函数柯里化
* 传递给函数一部分参数用于功能调用, 让他返回一个函数去处理剩下的参数
*/
let add = (x, y) => x +y;
let add2 = x=> (y => x+y);
// 柯里化
console.log(add2(2));//[Function (anonymous)]
console.log(add2(200));// [Function (anonymous)]
let limit3 = y => ( x => x > y )
let limit18 = limit3(18);
console.log(limit18(20));
// 柯里化 是一种预加载方式
/**
* 问题:包心菜产生
*/
// h(g(y(x)))
/**
* 组合
* 通过更优雅的方式实现纯函数的解耦
*/
let compose = (f, g) => ( x => f(g(x)));
let add1 = x => x+1;
let mul5 = y => y*5;
console.log(compose(add1, mul5)(2));// 11
console.log(compose(mul5, add1)(2));// 15
// 面试题- 数组长度未知,拿到最后一项
let first = arr => arr[0];
let reverse = arr => arr.reverse();
let last = compose(first, reverse);
console.log(last([1, 2, 3, 4])); // 4
es6:Object常用放过
Object.freeze();// 只冻结对象的根元素
Object.create();//
Object.keys();
Object.assign();
Object.values();?
Object.is();