原型深入(26)

 * 函数有三种角色

 *   1.普通函数

 *     ->堆栈内存释放

 *     ->作用域链

 *

 *   2.类

 *     ->prototype:原型

 *     ->__proto__:原型链

 *     ->实例

 *

 *   3.普通对象

 *     ->和普通的一个OBJ没啥区别,就是对键值对的增删改查

 *

 *   =>三种角色间没有什么必然关系

/*function Fn() {
    var n = 10;
    this.m = 100;
}
Fn.prototype.aa = function () {
    console.log('aa');
};
Fn.bb = function () {
    console.log('bb');
};*/
//=>普通函数
// Fn();//=>this:window  有一个私有变量n  和原型以及属性bb没有关系

//=>构造函数执行
// var f = new Fn;//=>this:f
// console.log(f.n);//=>undefined:n是私有变量和实例没有关系
// console.log(f.m);//=>100 实例的私有属性
// f.aa();//=>实例通过__proto__找到Fn.prototype上的方法
// console.log(f.bb);//=>undefined:bb是把Fn当做一个普通的对象设置的属性而已,和实例等没有半毛钱关系

//=>普通对象
// Fn.bb();

//=>JQ这个类库中提供了很多的方法,其中有一部分是写在原型上的,有一部分是把它当做普通对象来设置的
~function () {
    function jQuery() {
        //...
        return [JQ实例]
    }
    jQuery.prototype.animate=function(){}
    //...
    jQuery.ajax=function(){}
    //....
    window.jQuery = window.$ = jQuery;
}();
// $().ajax() //=>调不了
// $().anaimte() //=>这样可以调取
// $.ajax() //=>直接的对象键值对操作
// $.animate() //=>对象上没有animate这个属性,这个属性在和实例相关的原型上

例题分析

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

Foo.getName();//=>2 把Foo当做一个对象,找Foo的私有方法执行
getName();//=>4 执行全局下的GET-NAME
Foo().getName();//=>1 先把FOO当做普通函数执行,执行返回的结果在调取GET-NAME执行
getName();//=>1 执行的依然是全局下的GET-NAME

console.log(new Foo.getName());;//=>A:(Foo.getName) =>new A()  =>2
new Foo().getName();//=>B:new Foo() =>B.getName() =>3
console.log(new new Foo().getName());
//=>C:new Foo() =>new C[Foo的实例].getName() =>D:C.getName =>new D(); =>3  (先计算new Foo()创建一个实例f,然后new f.getName(),先找到f.getName,在把这个函数new一下,最后其实相当于把f.getName当做一个类,返回这个类的一个实例)

 原型链机制最终版(Function)

function Fn(){
    this.n=100;
}
Fn.prototype.getN = function () {
    console.log (this.n);
};
Fn.AA =200;
var f=new Fn ();

 深入理解原型和CALL

/*
 * 用来改变某一个函数中THIS关键字指向的
 *  call
 *  apply
 *  bind
 */
window.name = '珠峰';
let fn = function () {
    console.log(this.name);
};
let obj = {
    name: "OBJ",
    fn: fn
};
let oo = {name: "OO"};
// fn();//=>this:window "珠峰"
// obj.fn();//=>this:obj "OBJ"
// fn.call(oo)//=>this:oo

 * call
 *  1. [fn].call([this],[param]...)
 *   fn.call:当前实例(函数FN)通过原型链的查找机制,找到Function.prototype上的call方法  =>function call(){[native code]}
 *   fn.call():把找到的call方法执行
 *
 *   当call方法执行的时候,内部处理了一些事情
 *    =>首先把要操作函数中的THIS关键字变为CALL方法第一个传递的实参值
 *    =>把CALL方法第二个及第二个以后的实参获取到
 *    =>把要操作的函数执行,并且把第二个以后的传递进来的实参传给函数

// fn.call(oo);//=>this:oo
// fn.call(obj,10,20,30);//=>this:obj

/*Function.prototype.call = function () {
    let param1 = arguments[0],
        paramOther = [];//=>把ARG中除了第一个以外的实参获取到

    //=>this:fn 当前要操作的函数(函数类的一个实例)
    //把FN中的THIS关键字修改为PARAM1 =>把THIS(CALL中)中的this关键字修改为param1

    //=>把fn执行,把paramOther分别传递给fn
    // this(paramOther)  =>fn(paramOther)
};
fn.call({name:'xx'})
sum.call({..})
*/

let sum=function(a,b){
    console.log(this);//=>opt
};
let opt={n:20};

// sum.call(opt,20,30);//=>call执行 call中的this是sum  把this(call中的)中的“this关键字”改为opt 把this(call中的)执行,把20,30分别传递给它 //=>sum中this:opt  a=20 b=30

sum.call.call(opt)
//1.sum.call 找到Function.prototype上的call方法(也是一个函数,也是函数类的一个实例,也可以继续调用call/apply等方法)  =>A(函数)
//2.A.call(opt)  继续找到原型上的call方法,把call方法执行:把A中的this关键字修改为opt,然后把A执行
/*
Function.prototype.call=function callAA(){
    //=>1.把THIS(FN)中的"THIS关键字"修改为第一个参数值(OBJ)
    //=>2.把THIS(FN)执行,把第二个及以后接受的参数值传递给函数(10,20)
    //this(10,20)
};
fn.call(obj,10,20)
*/
function fn1(){console.log(1);}
function fn2(){console.log(2);}

//=>fn1.call.call.call.call===Function.prototype.call

fn1.call(fn2);//=>找到CALL-AA把它执行,CALL-AA中的THIS是FN1,第一个参数传递的是FN2  =>在CALL-AA中执行的是FN1 =>1

fn1.call.call(fn2);//=>找到CALL-AA让它执行,CALL-AA中的THIS是FN1.CALL,第一个参数是FN2  (把FN1.CALL中的THIS变为FN2,再让FN1.CALL执行  =>先找到CALL-AA,把它执行,只不过此时它中的THIS是FN2 =>让FN2中的THIS变为UNDEFINED,因为执行FN1.CALL的时候没有传递参数值,然后让FN2执行)  =>2

Function.prototype.call(fn1);//=>先找到CALL-AA把它执行,它中的THIS是Function.prototype =>让F.P中的THIS变为FN1,然后让F.P执行,F.P是一个匿名函数也是一个空函数,执行没有任何的输出

Function.prototype.call.call(fn1);//=>先找到CALL-AA把它执行,它中的THIS是F.P.CALL =>把F.P.CALL中的THIS修改为FN1,让F.P.CALL执行  =>F.P.CALL(CALL-AA)第二次把它执行(此时它里面的THIS已经是FN1) =>这一次其实在CALL-AA中是让FN1执行 =>1
//<==> fn1.call.call(fn2)
//<==> fn1.call===Function.prototype.call :true

fn1.call.call.call.call.call(fn2);

 call、apply、bind三者的区别

"use strict";

let fn = function (a, b) {
    console.log(this);
};
let obj = {name: "OBJ"};

/*
 * CALL中的细节
 *   1.非严格模式下,如果参数不传,或者第一个传递的是null/undefined,THIS都指向WINDOW
 *   2.在严格模式下,第一个参数是谁,THIS就指向谁(包括null/undefined),不传THIS是undefined
 */
// fn.call(obj, 10, 20);//=>this:obj a=10 b=20
// fn.call(10, 20);//=>this:10 a=20 b=undefined
// fn.call();//=>this:window a=undefined b=undefined
// fn.call(null);//=>this:window
// fn.call(undefined);//=>this:window

案例需求

// document.onclick = fn;//=>把FN绑定给点击事件,点击的时候执行FN
// document.onclick = fn();//=>在绑定的时候,先把FN执行,把执行的返回值(UNDEFINED)绑定给事件,当点击的时候执行的是undefined

//=>需求:点击的时候执行FN,让FN中的THIS是OBJ
// document.onclick = fn;//=>this:document
// document.onclick = fn.call(obj);//=>虽然this确实改为obj了,但是绑定的时候就把fn执行了(call是立即执行函数),点击的时候执行的是fn的返回值undefined
// document.onclick = fn.bind(obj);//=>bind属于把fn中的this预处理为obj,此时fn没有执行,当点击的时候才会把fn执行
/*
 * 需求一:获取数组中的最大值(最小值)
 *   1.给数组先排序(由大到小排序),第一项就是最大值
 *   2.假设法:假设第一个值是最大值,依次遍历数组中后面的每一项,和假设的值进行比较,如果比假设的值要大,把当前项赋值给MAX...
 *   3.基于Math.max完成
 */
let ary = [12, 13, 14, 23, 24, 13, 15, 12];

//=>排序
/*
let max = ary.sort(function (a, b) {
    return b - a;
})[0];
console.log(max);
*/

//=>假设法
/*
let max = ary[0];
for (let i = 1; i < ary.length; i++) {
    let item = ary[i];
    item > max ? max = item : null;
}
console.log(max);
*/

//=>Math.max
// console.log(Math.max(ary));//=>NaN =>Math.max是获取一堆数中的最大值,需要我们把比较的数,一个个的传递给这个方法 =>Math.max(12,13,14...) =>Math.max([12,13,14...])这样只是传递一个值
/*
[12,13,14].toString() =>"12,13,14"
eval("12,13,14") =>14

   1.eval:把字符串转换为JS表达式
    eval("1+2") =>3

   2.括号表达式(小括号的应用)
    用小括号包起来,里面有很多项(每一项用逗号分隔),最后只获取最后一项的内容
(但是会把其它的项也都过一遍)
    (function(){
        console.log(1);
    },function(){
        console.log(2);
    })();
    =>2

    let a=1===1?(12,23,14):null;
    =>a=14

   不建议大家过多使用括号表达式,因为会改变THIS
*/
/*let fn=function(){console.log(this);}
let obj={fn:fn};
(fn,obj.fn)();//=>执行的是第二个OBJ.FN,但是方法中的THIS是WINDOW而不是OBJ
(obj.fn)();//=>this:obj*/

//=>基于EVAL转换字符串为JS表达式
// console.log(eval("Math.max(" + ary.toString() + ")"));

//=>利用了APPLY的一个特征:虽然放的是一个数组,但是执行方法的时候,也是把数组中的每一项一个个的传递给函数
// console.log(Math.max.apply(null, ary));

把类数组转换为数组

/*
 * 编写一个方法fn,实现任意数求平均数(去除数字中的最大和最小,然后再算平均数,保留小数点后面两位)
 */
// let fn = function () {
//     //=>arguments:类数组(不能直接调取数组原型上的方法)
//     //1.先给ARGUMENTS排序(不能直接使用SORT方法),把排序后的值去掉首位(干掉最大值和最小值)
//     //2.把剩下的值求和,除以总长度,求出平均数即可
//
//     //arguments.sort()//=>Uncaught TypeError: arguments.sort is not a function
//     //=>把ARG类数组转换为数组ARY
//     let ary = [];
//     for (let i = 0; i < arguments.length; i++) {
//         ary.push(arguments[i]);
//     }
//
//     //=>给ARY排序,去除首位
//     ary.sort(function (a, b) {
//         return a - b;
//     });
//     ary.pop();
//     ary.shift();
//
//     //=>然后再求和,最后求平均
//     let total = 0;
//     for (let i = 0; i < ary.length; i++) {
//         total += ary[i];
//     }
//     return (total / ary.length).toFixed(2);
// };
//=>重写数组的SLICE方法,实现:ary.slice()相当于把ARY克隆一份新数组
Array.prototype.mySlice = function () {
    //=>把操作的数组ARY克隆一份
    //=>this:ary
    //=>内置的SLICE实现数组克隆的代码
    let newAry = [];
    for (let i = 0; i < this.length; i++) {
        newAry.push(this[i]);
    }
    //=>如果我们把内置的SLICE执行,并且让方法中的THIS指向ARGUMENTS,就相当于把ARG转换为数组
    /*
        let ary = [];
        for (let i = 0; i < arguments.length; i++) {
            ary.push(arguments[i]);
        }
        =>把ARG这个类数组转换为数组
     */
    return newAry;
};
let ary = [12, 23, 34];
console.log(ary.mySlice());//=>[12, 23, 34]

let fn = function () {
    //=>把ARG类数组转换为数组ARY(把类数组克隆一份一模一样的,最后存储到数组中)  =>数组的SLICE可以实现克隆的
    //=>把内置的SLICE方法执行 Array.prototype.slice() / [].slice() ...
    let ary = [].slice.call(arguments, 0);//=>类数组借用数组原型上的方法执行,实现相关的操作(借用SLICE实现把类数组转换为数组)  前提:类数组和数组类似,都有length和索引(字符串也符合这个前提,所以也可以这样搞)

   /* [].sort.call(arguments, function (a, b) {
        return a - b;
    });//=>借用SORT给ARG排序,除此之外其它的很多数组的方法都可以被ARG借用*/

    ary.sort(function (a, b) {
        return a - b;
    }).pop();
    ary.shift();
    return (eval(ary.join('+')) / ary.length).toFixed(2);
};
console.log(fn(10, 9.8, 9.5, 8.7, 8.8, 8, 9.2, 8.9));

 基于ES6的方式把类数组转换为数组

/*let fn = function () {
    // let ary = [...arguments];//=>把类数组转换为数组
    let ary = Array.from(arguments);//=>把类数组转换为数组
    ary.sort(function (a, b) {
        return a - b;
    }).pop();
    ary.shift();
    return (eval(ary.join('+')) / ary.length).toFixed(2);
};*/
let fn = function (...ary) {
    ary.sort(function (a, b) {
        return a - b;
    }).pop();
    ary.shift();
    return (eval(ary.join('+')) / ary.length).toFixed(2);
};
console.log(fn(10, 9.8, 9.5, 8.7, 8.8, 8, 9.2, 8.9));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值