前端基础知识-示例代码2.0

1.0链接:前端基础知识-示例代码

21.一像素

伪类 + transform 实现
  • 原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。
  • 单条border样式设置
.scale-1px{
  position: relative;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  bottom: 0;
  background: #000;
  width: 100%;
  height: 1px;
  -webkit-transform: scaleY(0.5);
  transform: scaleY(0.5);
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
}
  • 四条boder样式设置
.scale-1px{
  position: relative;
  margin-bottom: 20px;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  border: 1px solid #000;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  width: 200%;
  height: 200%;
  -webkit-transform: scale(0.5);
  transform: scale(0.5);
  -webkit-transform-origin: left top;
  transform-origin: left top;
}

22.动画

animation:mymove 5s infinite;

@keyframes mymove {
    from {top:0px;}
    to {top:200px;}
}
  • js实现一个持续的动画效果
//兼容性处理
window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          function(callback){
            window.setTimeout(callback, 1000 / 60);
          };
})();

var e = document.getElementById("e");
var flag = true;
var left = 0;

function render() {
    left == 0 ? flag = true : left == 100 ? flag = false : '';
    flag ? e.style.left = ` ${left++}px` :
        e.style.left = ` ${left--}px`;
}

(function animloop() {
    render();
    requestAnimFrame(animloop);
})();

23. 实现sum(2)(3)

// 写一个 function 让下面两行代码输出的结果都为 5
console.log(sum(2, 3));
console.log(sum(2)(3));

// 实现
function sum() {
    var cache;
    if (arguments.length === 1) {
        cache = arguments[0];
        return function ( number ) {return cache + number;};
    }
    else return arguments[0] + arguments[1];
};

24. 函数柯里化

  • 函数柯里化:将能够接收多个参数的函数转化为接收单一参数的函数,并且返回接收余下参数或结果的新函数的技术。
  • 主要作用和特点就是参数复用、提前返回和延迟计算/执行
参数复用
// 普通函数
function add(x,y){
    return x + y;
}

add(3,4);   //5

// 实现了柯里化的函数
// 接收参数,返回新函数,把参数传给新函数使用,最后求值
let add = function(x){
    return function(y){
        return x + y;
    }
};

add(3)(4);  // 7

通用的柯里化函数

function curry(fn) {
    let slice = Array.prototype.slice,  // 将slice缓存起来
        args = slice.call(arguments, 1);   // 这里将arguments转成数组并保存
        
    return function() {
        // 将新旧的参数拼接起来
        let newArgs = args.concat(slice.call(arguments));    
        return fn.apply(null, newArgs); // 返回执行的fn并传递最新的参数
    }
}
if (typeof Function.prototype.bind === "undefined"){
  Function.prototype.bind = function (thisArgs){
    var fn = this,
        slice = Array.prototype.slice,
        args = slice.call(arguments, 1);
    return function (){
      let newArgs = args.concat(slice.call(arguments))
      return fn.apply(thisArgs, newArgs);
    }
  }
}
题目:需要写一个函数,满足
// curry(fn)(1)(2)(3) //6
var fn = function(a,b,c) {
    return a+b+c;
}

function curry(fn) {
    var arr = [],
    mySlice = arr.slice
    fnLen = fn.length;

    function curring() {
        arr = arr.concat(mySlice.call(arguments));
        if(arr.length < fnLen) {
            return curring;
        }
        return fn.apply(this, arr);
    }
  return curring;
}
curry(fn)(1)(2)(3);//6
延迟计算/运行

ES5中的bind方法

if (!Function.prototype.bind) {
    Function.prototype.bind = function(context) {
        var self = this,
            args = Array.prototype.slice.call(arguments);
            
        return function() {
            return self.apply(context, args.slice(1));    
        }
    };
}

25.手写一个 bind 方法

// 带一个参数
Function.prototype.bind = function(context) {
    let self = this,
        slice = Array.prototype.slice,
        args = slice.call(arguments);
        
    return function() {
        return self.apply(context, args.slice(1));    
    }
};

// 带多个参数
//ES3实现
if(!Function.prototype.bind){
    Function.prototype.bind = function(o, args){
        
        var self = this,
            boundArgs = arguments;//注:arguments是指sum.bind(null,1)中的参数null和1
        
        return function(){                 //此时返回的只是一个函数
            var args = [], i;
            for(var i=1; i< boundArgs.length; i++){  
                 args.push(boundArgs[i]);
            }
            for(var i =0; i< arguments.length; i++){
                 args.push(arguments[i]);//注:这里的arguments是指result(2)中的参数2
            }
            return self.apply(o, args);    
        }
    }
}
// 或者
// 代码来自书籍 《javaScript 模式》
if (typeof Function.prototype.bind === "undefined"){
  Function.prototype.bind = function (thisArgs){
    var fn = this,
        slice = Array.prototype.slice,
        args = slice.call(arguments, 1);
    return function (){
      return fn.apply(thisArgs, args.concat(slice.call(arguments)));
    }
  }
}
//注:前后arguments不是一回事哦~


//调用
var sum = function(x,y){ return x+y };
var result = sum.bind(null,1);
result(2);   // 3

// 或者
Function.prototype.bind = function(){
    var fn = this;
    var args = Array.prototye.slice.call(arguments);
    var context = args.shift();

    return function(){
        return fn.apply(context, args.concat(Array.prototype.slice.call(arguments)));
    };

26.经典面试问题:new 的过程

  • 创建一个空对象 obj;
  • 将新创建的空对象的隐式原型指向其构造函数的显示原型。
  • 使用 call 改变 this 的指向
  • 如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接直接返回该对象。
var NEW = function(func) {
    var o = Object.create(func.prototype)
    var k = func.call(o)
    if (typeof k === 'object') {
        return k
    } else {
        return o
    }
}

27.javascript里面的继承怎么实现,如何避免原型链上面的对象共享

  • 原型链:当一个引用类型继承另一个引用类型的属性和方法时候就会产生一个原型链。
// ES5:寄生组合式继承:通过借用构造函数来继承属性和原型链来实现子继承父。
    function ParentClass(name) {
      this.name = name;
    }
    ParentClass.prototype.sayHello = function () {
      console.log("I'm parent!" + this.name);
    }
    function SubClass(name, age) {
      //若是要多个参数可以用apply 结合 ...解构
      ParentClass.call(this, name);
      this.age = age;
    }
    SubClass.prototype.sayChildHello = function (name) {
      console.log("I'm child " + this.name)
    }
    
    SubClass.prototype = Object.create(ParentClass.prototype);
    SubClass.prototype.constructor = SubClass;

    let testA = new SubClass('CRPER')

    // Object.create()的polyfill
    /*
    function pureObject(obj){
        //定义了一个临时构造函数
         function F() {}
         //将这个临时构造函数的原型指向了传入进来的对象。
         F.prototype = obj;
         //返回这个构造函数的一个实例。该实例拥有obj的所有属性和方法。
         //因为该实例的原型是obj对象。
         return new F();
    }
    */
    
    或
    
    function subClass() {
        superClass.apply(this, arguments);
        this.abc = 1;
    }

    function inherits(subClass, superClass) {
        function Inner() {}
        
        Inner.prototype = superClass.prototype;
        subClass.prototype = new Inner();
        subClass.prototype.constructor = subClass;
    }
    
    inherits(subClass, superClass);
    
    subClass.prototype.getTest = function() {
        console.log("hello")
    };
// ES6: 其实就是ES5的语法糖,不过可读性很强..
    class ParentClass {
      constructor(name) {
        this.name = name;
      }
      sayHello() {
        console.log("I'm parent!" + this.name);
      }
    }

    class SubClass extends ParentClass {
      constructor(name) {
        super(name);
      }
      sayChildHello() {
        console.log("I'm child " + this.name)
      }
      // 重新声明父类同名方法会覆写,ES5的话就是直接操作自己的原型链上
      sayHello(){
        console.log("override parent method !,I'm sayHello Method")
      }
    }

    let testA = new SubClass('CRPER')

28.继承 JS 内置对象(Date)

ES5

// 需要考虑polyfill情况
Object.setPrototypeOf = Object.setPrototypeOf ||
function(obj, proto) {
    obj.__proto__ = proto;

    return obj;
};

/**
 * 用了点技巧的继承,实际上返回的是Date对象
 */
function MyDate() {
    // bind属于Function.prototype,接收的参数是:object, param1, params2...
    var dateInst = new(Function.prototype.bind.apply(Date, [Date].concat(Array.prototype.slice.call(arguments))))();

    // 更改原型指向,否则无法调用MyDate原型上的方法
    // ES6方案中,这里就是[[prototype]]这个隐式原型对象,在没有标准以前就是__proto__
    Object.setPrototypeOf(dateInst, MyDate.prototype);

    dateInst.abc = 1;

    return dateInst;
}

// 原型重新指回Date,否则根本无法算是继承
Object.setPrototypeOf(MyDate.prototype, Date.prototype);

MyDate.prototype.getTest = function getTest() {
    return this.getTime();
};

let date = new MyDate();

// 正常输出,譬如1515638988725
console.log(date.getTest());

ES6

class MyDate extends Date {
    constructor() {
        super();
        this.abc = 1;
    }
    getTest() {
        return this.getTime();
    }
}

let date = new MyDate();

// 正常输出,譬如1515638988725
console.log(date.getTest());

注意:这里的正常输出环境是直接用ES6运行,不经过babel打包,打包后实质上是转化成ES5的,所以效果完全不一样,会报错的

29.简易双向数据绑定


  
  

30.扁平化后的数组

如:[1, [2, [ [3, 4], 5], 6]] => [1, 2, 3, 4, 5, 6]

    var data =  [1, [2, [ [3, 4], 5], 6]];

    function flat(data, result) {
        var i, d, len;
        for (i = 0, len = data.length; i < len; ++i) {
            d = data[i];
            if (typeof d === 'number') {
                result.push(d);
            } else {
                flat(d, result);
            }
        }
    }

    var result = [];
    flat(data, result);

    console.log(result);

31.冒泡排序

  • 比较相邻的两个元素,如果前一个比后一个大,则交换位置。
  • 第一轮的时候最后一个元素应该是最大的一个。
  • 按照步骤一的方法进行相邻两个元素的比较,这个时候由于最后一个元素已经是最大的了,所以最后一个元素不用比较。
function bubble_sort(arr){
  for(var i = 0;i < arr.length - 1; i++){
    for(var j = 0;j < arr.length - i - 1;j++){
      if(arr[j] > arr[j+1]){
        [arr[j], arr[j+1]] = [arr[j + 1], arr[j]]
      }
    }
  }
}

var arr = [3,1,5,7,2,4,9,6,10,8];
bubble_sort(arr);
console.log(arr);

32.快速排序

快速排序是对冒泡排序的一种改进

  • 第一趟排序时将数据分成两部分,一部分比另一部分的所有数据都要小。
  • 然后递归调用,在两边都实行快速排序。
function quick_sort(arr){
  if(arr.length <= 1){
    return arr;
  }
  var pivotIndex = Math.floor(arr.length / 2);
  var pivot = arr.splice(pivotIndex, 1)[0];

  var left = [];
  var right = [];
  for (var i = 0;i < arr.length; i++) {
    if(arr[i] < pivot){
      left.push(arr[i]);
    } else {
      right.push(arr[i]);
    }
  }

  return quick_sort(left).concat([pivot],quick_sort(right));
}

var arr=[5,6,2,1,3,8,7,1,2,3,4,7];
console.log(quick_sort(arr));

33.选择排序

  • 找到最小的放在第一位,找到第二小的放在第二位,以此类推 算法复杂度O(n^2)
function selectionSort(arr) {
    let len = arr.length;
    let minIndex;
    for (let i = 0; i < len - 1; i++) {
        minIndex = i;
        for (let j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     //寻找最小的数
                minIndex = j;                 //将最小数的索引保存
            }
        }
        [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
    }
return arr;
}

34.插入排序

  • 从第一个元素开始,该元素可以认为已经被排序
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描
  • 如果该元素(已排序)大于新元素,将该元素移到下一位置
  • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  • 将新元素插入到下一位置中
  • 重复步骤2
function insert_sort(arr){
  var i=1,
  j,key,len=arr.length;
  for(;i-1){
      if(arr[j]>key){
        arr[j+1]=arr[j];
      }else{
        break;
      }
    }

    arr[j+1]=key;
  }

  return arr;
}

function insert_sort(arr) {
    let len = arr.length;
    let preIndex, current;
    for (let i = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while (preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex + 1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex + 1] = current;
    }
    return arr;
}

insert_sort([2,34,54,2,5,1,7]);

原文链接:前端基本功-示例代码 (二)

你可能感兴趣的:(前端基础知识-示例代码2.0)