第十七章 类数组转数组、apply&bind手写实现及class简介

118 阅读4分钟

一、类数组转数组方式

1、展开运算符实现

  • 代码 :[...类数组]
  • 工作中经常使用

2、利用数组方法slice与call结合实现

  • 代码 :[].slice.call(arguments)
  • **原理:
    **先通过数组找到slice方法,
    通过call让这个slice执行,并把其中的this变成类型组

3、创建一个空数组通过循环把类数组放入

  • 代码:
var ary = [];
        for (var i = 0; i < arguments.length; i++) {
            ary[i]=arguments[i];
        }
  • 最基础方式

4、Array.from()

  • 代码:Array.from(xxx)
  • 返回值:返回值是一个数组,项跟xxx一样

5、封装一个方法

 function toArray(arg){
                try{
                    return [].slice.call(arg)
                }catch(error){
                    var ary = [];
                    for(var i = 0;i<arg.length;i++){
                        ary.push(arrg[i])
                    }
                    return ary
                }
            }

try{}catch(){}

try{   
  //要执行可能会报错的代码
}catch(err){
	//形参对应的是 上边运行的错误信息
	//上边运行出错,机会走到这个catch,但是不会影响主体代码执行
	console.log(err)//返回报错代码
}    //报错时不会阻碍下方代码执行

6、...知识点

  • **Array.isArray(xxx)
    **作用:用来判断xxx是不是一个数组
  • Object.keys(obj)
    作用:把obj所有的私有属性名拿出来
    目的:循环对象\
var obj = {q:1,w:2;j:3,f:4}
Object.keys(obj).forEach(k=>{
 	console.log('属性名',k,'属性值',obj[k]);
})

//或者
for(let k in obj){
	if(obj.hasOwnProperty(k)){      //加判断是为了排除它的公有属性
		console.log('属性名',k,'属性值',obj[k]);
		}
}
  • try{}catch(){}
try{   
  //要执行可能会报错的代码
}catch(err){
	//形参对应的是 上边运行的错误信息
	//上边运行出错,机会走到这个catch,但是不会影响主体代码执行
	console.log(err)//返回报错代码
}    //报错时不会阻碍下方代码执行

二、手写实现

this复习

  • 全局下的this是window
  • 自执行函数this是window
  • 箭头函数的this是上一级作用域中的this !!!!
  • 定时器中的this是window 但若定时器中的函数为箭头函数,则依照箭头函数的规则!
  • 事件绑定对应函数中的this是 绑定的元素
  • 其他看点 点前边是谁this就是谁,没点就是window
  • new执行类的时候 其中的this就是实例
  • 类原型上的方法中的this得是实例
  • call可以改变函数中的this指向 f.call(111)
    apply和bind都是改变函数中this指向的

1、实现call

  • 关键是实现call功能
function fn(q,w,e){
        console.log(this);
        console.log(q,w,e);
        return 888
    }
    var obj = {
        name:'珠峰'
    }
    Function.prototype.myCall = function(target,...arg){
        //this-->fn
        let fn = this;//把fn执行,然后把其中的this换成target
        let a = Symbol('mycall')
        target[a] = fn;//res是fn的执行结果
        let res = target[a](...arg);//myCall的执行结果就是fn的执行结果
        delete target[a];//删除不掉,在删除之前就已经被打印出来,规避不了
        return res;
    }
    let res = fn.call(obj,1,2,3);//res-->888
    let res2 = fn.myCall(obj,1,2,3)//res-->888

2、apply

  • 代码 fn.apply(xxx,[a,b,c,d])
  • 返回值:前边函数改变this后的执行结果
  • 用法:
    功能跟call一样;
    区别在于call执行的时候,给函数传的实参是散开写的
    apply执行的时候,给函数传的实参是一个集合的方式

【手写实现apply】

 //手写apply
     Function.prototype.myApply = function(target,arg){
         let fn =this;
         let a = Symbol('myapply');
         target[a] = fn;
         let res = target[a](...arg);
         delete target[a];
         return res;
     }

3、bind

  • 代码:fn.bind(xxx,a,b,c,d)
    第一个参数为this的指向,后边为要传递的实参
  • 返回值:一个新的函数
  • 白话:bind会返回一个新函数,新函数执行的时候,会让函数fn执行,并且把fn中的this换成obj,后边的实参传给fn (新函数执行的时候,把新函数传递给实参会放到之前传递的实参后边)
function fn(){
        console.log(arguments);
        console.log(this);
    }
    var obj ={
        name :'珠峰'
    }
    let res1 = fn.bind(obj,2,2,5,4,3)//返回的是一个新的函数,并不会使fn执行,新函数执行时才会让fn执行
    let res2 = fn.call(obj,2,2,5,4,3)

【手写实现bind】

//手写bind
    Function.prototype.myBind = function (target,...arg){
        
        return (...arg2)=>{
        let fn = this;
        let a = Symbol('mybind')
        target[a] = fn;
        let res = target[a](...arg,...arg2);
        return res;
        }
    }
    let res3 = fn.myBind(obj,2,2,5,4,3)

4、create

  • 代码:obj = Object.create([])
  • 返回值:空对象
  • 作用:Object.create 执行会返回一个空对象,这个对象的__proto__是指向create的参数(然后就可以用参数的方法)
//参照理解
var obj = new Object();
var obj2 = {};
var obj3 = Object.create(null);
var obj4 = Object.create([]);
        console.log(obj,obj2,obj3,obj4);
//Object.create 执行会返回一个空对象,这个对象的__proto__是指向create的参数

【手写实现create】

//实现
        function create(target){
            //var obj ={}
            //obj.__proto__ = target//__proto__可能会有报错的风险,可以把她的类的原型改成需要的东西(原型重写)
            function QQQ(){
                QQQ.prototype = target;
                let a = new QQQ;
                return a
            }    //内置类的原型不能修改
            return obj
        }
        var obj4 = create([])

三、class(es6新增)

1、class简介

class是es6新增的一个用来创造类的方式,创造出来的类不能当作普通函数执行。

2、用法

  • 代码:class 类名{ constructor(形参){ 私有属性 } 共有属性 }
  • static:静态属性 可以直接加到函数自己身上而不是原型上
 class Person {
            constructor(name, age, sex) { //这的小括号是接受形参的
                console.log(arguments);
                this.name = name;
                this.age = age;
                this.sex = sex;
            }
            eat(food) {
                console.log(`吃${food}`);
            }
            height = 100 //私有属性的简写方式(如果是常数的情况下)

            static qqq = 888 //静态属性   指的是Person自己属性
            static ttt = 899 // Person的prototype是它的一个内置的静态属性;
            static uuu = 222
        }
        let p1 = new Person('小孔', 12, 0)
        
        
 //对比
 function Person(name,age,sex){
        this.name = name ;
        this.age = age;
        this.sex = sex;
    } 
    Person.prototype.eat = function(food){
        console.log(`吃${food}`)
        Person.ttt = 888//静态属性
    }

四、练习题

// 变量提升阶段  
// 声明+定义了Foo  
// 声明了一个getName
// ——声明—— +定义getName 5
getName() // 5

function Foo() {
  getName = function () { console.log(1); };
  return this;
}
Foo.getName = function () { console.log(2); }
Foo.prototype.getName = function () { console.log(3); }
var getName = function () { console.log(4); }
function getName() { console.log(5); }

getName(); // 4

Foo.getName(); // 2

Foo().getName();// window.getName()  1
//Foo() 把window下的getName 从log4变成了log1函数
// window.getName()


var a = { aa: 23 }
b = a;
b.x = a = {}

getName(); //1
let p1 = new Foo.getName();
// 点比new优先级高;先通过Foo找到getName,然后new执行getName

let p2 = new Foo().getName();
// 小括号的优先级是最大的;先new执行了Foo 得到的实例再去找到getName 然后执行

let p3 = new new Foo().getName();
// 先new Foo 得到实例之后找到getName  然后再通过new执行getName