一篇‘边学边记’的前端知识巩固笔记

411 阅读4分钟

前言

记录一些知识,方便巩固,不定时更新。

简单的3D动画

参考链接: 文档 | 文档演示 | 参考

知识点:

perspective 属性定义 3D 元素距视图的距离,以像素计。

当为元素定义 perspective 属性时,其子元素会获得透视效果,而不是元素本身。

perspective 属性只影响 3D 转换元素。

与 perspective-origin 属性一同使用该属性,能够改变 3D 元素的底部位置。
 

transform-style: flat | preserve-3d; 属性指定嵌套元素是怎样在三维空间中呈现。

flat	表示所有子元素在2D平面呈现。

preserve-3d	表示所有子元素在3D空间中呈现。

transform 属性应用于元素的2D或3D转换。

演示:代码演示

transform.gif

简单的拖拽

参考链接: 理论参考 | 实践参考

知识点:

鼠标事件:

  • onmousedown 按下

  • onmousemove 移动

  • onmouseup 鼠标按键被松开

原理:元素按下时,监听文档拖拽位置,松开清空。

Mouse事件的event对象

  • offsetX/offsetY 相对于“自身”的水平/垂直坐标

  • clientX/clientY 相对于“当前窗口”的水平/垂直坐标 (不包含滚动距离)

  • pageX/pageY 相对于“文档”的水平/垂直坐标 (包含滚动距离)

  • screenX/screenY 相对于“屏幕”的水平/垂直坐标

演示:代码演示

drag.gif

事件循环 EventLoop

原作者讲的很详细,具体请参考:原文链接

事件循环Event Loop,这是目前浏览器和NodeJS处理JavaScript代码的一种机制,而这种机制存在的背后,就有因为JavaScript是一门单线程的语言。

  • 调用栈的概念
  • 同步任务 / 异步任务 之间的差别和不同的执行机制
  • 任务队列 (任务入队、宏任务/微任务)
  • 事件循环的具体流程

事件循环的具体流程如下:

  1. 从宏任务队列中,按照入队顺序,找到第一个执行的宏任务,放入调用栈,开始执行;
  2. 执行完该宏任务下所有同步任务后,即调用栈清空后,该宏任务被推出宏任务队列,然后微任务队列开始按照入队顺序,依次执行其中的微任务,直至微任务队列清空为止
  3. 当微任务队列清空后,一个事件循环结束;
  4. 接着从宏任务队列中,找到下一个执行的宏任务,开始第二个事件循环,直至宏任务队列清空为止。

这里有几个重点:

  1. 当我们第一次执行的时候,解释器会将整体代码script放入宏任务队列中,因此事件循环是从第一个宏任务开始的;

  2. 如果在执行微任务的过程中,产生新的微任务添加到微任务队列中,也需要一起清空;微任务队列没清空之前,是不会执行下一个宏任务的。

JavaScript call和apply 的模拟实现

原作者讲的很详细,具体请参考:冴羽大大的 - JavaScript深入之call和apply的模拟实现

O M G! 我真是不得了哇,这我都看懂了,先给自己鼓个掌,哈哈哈哈哈.....,咳咳,赶紧记一下,不然过会就忘了。

一句话介绍 call :

call()方法在使用一个指定的 this 值,和若干个指定的参数值,的前提下,调用某个函数或方法。

示例:

    var foo = {
        value:1
    };
    function bar() {
        console.log(this.value)
    }
    
    bar.call(foo); // 1

两个知识点:

  1. call 改变了 this 的指向,指向到 foo
  2. bar 函数执行了

call 模拟实现步骤:

  1. 将函数设为对象的属性
  2. 执行该函数
  3. 删除该函数 另外再处理三种情况:
  4. this参数为null的情况
  5. 有多个参数的情况
  6. 函数有返回值的情况

call 模拟实现代码:

源码地址

Function.prototype.call2 = function(context){
    // 第一种情况:this 参数可以传 null,当为 null 的时候,视为指向 window
    var context = context || window;
    
    // 第一步:将函数设为对象的属性
    // 这里的this指向的是bar,因为this是在运行时绑定的
    // 根据 bar.call2(obj,'kevin',18) 调用得知 context.fn = this 相当于 obj.fn = bar
    context.fn = this;
    
    // 第二种情况:函数接受未知长度的参数
    // arguments = { 
    //    0:foo,
    //    1:'kevin',
    //    2:18,
    //    length:3
    // }
    // 利用 arguments 对象,接受所有参数,然后从第二个参数开始循环,
    // 因为第一个参数是传入的是要改变的this指向的参数,从第二位开始才是函数中要用到的参数。
    // 这里执行后 args 为 ["arguments[1]","arguments[2]"]
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++){
        args.push('arguments[' + args + ']');
    }
    
    // 第二步:执行 context.fn() 这个函数
    // eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。
    var result = eval('context.fn(' + args + ')')
    // 此时的写法相当于 context.fn(arguments[1],arguments[2]) 这样带参数的执行了一下。
    
    // 第三步:删除 context.fn() 函数
    delete context.fn;
    
    // 第三种情况:函数有返回值
    return result;
}



// 测试一下
var value = 2;

var obj = {
    value: 1
}

function bar(name,age){
    console.log(this.value)
    return {
        value: this.value,
        name: name,
        age: age
    }
}

bar.call2(null);  // 2  this参数为null,默认指向window

bar.call2(obj,'kevin',18)
// 1
// Object {
//    value: 1,
//    name: 'kevin'
//    age: 18
// }

apply的模拟实现:

源码地址

apply 的参数是放在数组里的。

Function.prototype.apply = function(context,arr){
    var context = Object(context) || window;
    context.fn = this;
    
    var result;
    if (!arr) {
        result = context.fn();
    } else {
        var args = [];
        for(var i = 0, len = arr.length; i < len; i++){
            args.push('arr[' + i + ']');
        }
        result = eval('context.fn(' + args + ')')
    }
    
    delete context.fn;
    return result;
}

未完待续......