Giter Site home page Giter Site logo

practice's People

Contributors

kitewhere avatar

Watchers

 avatar  avatar

practice's Issues

不科学的 toFixed

问题

当对浮点数进行取精度对四舍五入时 一般会使用 toFixed 方法
但是

n = 0.015
n.toFixed(2) === '0.02'  // false
n.toFixed(2) === '0.01'  // true

一脸懵逼对不对

mdn 上的解释

一个数值的字符串表现形式,不使用指数记数法,而是在小数点后有 digits(注:digits具体值取决于传入参数)位数字。该数值在必要时进行四舍五入,另外在必要时会用 0 来填充小数部分,以便小数部分有指定的位数。 如果数值大于 1e+21,该方法会简单调用 Number.prototype.toString()并返回一个指数记数法格式的字符串。

重点是这个 必要时 也就是说 即使 5 也可能不会进位
原因在于 toFixed 使用的不是简单的四舍五入法 而是银行家舍入法

银行家舍入法:

四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一

五前为奇要进一 看到这里是不是又一脸懵逼 0.015 5 前的 1 是奇数 应该进位啊
这个应该是由于浮点数的精度问题引起的 又是一个坑

总之结论就是 tofixed 无论那种舍入法 都有问题
那么要如何解决呢

结论

利用 Math.round() 进行标准的四舍五入

Math.round() 函数返回一个数字四舍五入后最接近的整数

const toFixed = (num, digits) => Math.round(num * (multiple = 10 ** digits)) / multiple + ''

toFixed(0.015, 2) === '0.02' // true

其他

顺带一提 取整的其他写法

const floor = num => num | 0;
const ceil = num => 1 + num | 0
const round = num => .5 + num | 0;

参考

http://www.chengfeilong.com/toFixed

ramda 中的 curry 函数

ramda 中的 curry 函数定义是这样的

var curry = _curry1(function curry(fn) {
  return curryN(fn.length, fn);
});

_curry1 和 curryN 是什么东西呢

curryN 里是这样的

var curryN = _curry2(function curryN(length, fn) {
  if (length === 1) {
    return _curry1(fn);
  }
  return _arity(length, _curryN(length, [], fn));
});

又多了 _curry2 _curryN _arity 而且 _curry2.js 边上还有 _curry3.js

这样一层套一层 最后任务落到 _curryN 的头上

_curryN 中间一大段其实都是在处理 Placeholder 的 先不管
关键的逻辑如下

return  left <= 0 ? fn.apply(this, combined)
                     : _arity(left, _curryN(length, combined, fn));

如果 参数都齐了 left <=0 那么执行 fn 否则执行 _arity
但是这里 不应该是直接返回 _curryN(length, combined, fn)
这个 _arity 是做什么的

_arity 里是这样的

function _arity(n, fn) {
  /* eslint-disable no-unused-vars */
  switch (n) {
    case 0: return function() { return fn.apply(this, arguments); };
    case 1: return function(a0) { return fn.apply(this, arguments); };
    case 2: return function(a0, a1) { return fn.apply(this, arguments); };
    case 3: return function(a0, a1, a2) { return fn.apply(this, arguments); };
    case 4: return function(a0, a1, a2, a3) { return fn.apply(this, arguments); };
    case 5: return function(a0, a1, a2, a3, a4) { return fn.apply(this, arguments); };
    case 6: return function(a0, a1, a2, a3, a4, a5) { return fn.apply(this, arguments); };
    case 7: return function(a0, a1, a2, a3, a4, a5, a6) { return fn.apply(this, arguments); };
    case 8: return function(a0, a1, a2, a3, a4, a5, a6, a7) { return fn.apply(this, arguments); };
    case 9: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8) { return fn.apply(this, arguments); };
    case 10: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) { return fn.apply(this, arguments); };
    default: throw new Error('First argument to _arity must be a non-negative integer no greater than ten');
  }
}

???shenmegui
这里其实是模拟 Function.length 的
如果不包 _arity 这一层 那么柯里化的函数 length 始终为 0

其实这里可以用 eval 之类的来模拟 但考虑到性能就写成了这样
一般函数参数也不太可能超过10个吧

const add = (a,b,c) => a+b+c;
add.length;   // 3

const curry_add = curry(add)

curry_add.length;   // 3   还剩下3个参数 如果没有 _arity 这里是 0 

curry_add(1).length;    // 2   还剩下2个参数  如果没有 _arity 这里也是 0

_curry1 _curry2 _curry3 其实就是提供了 _curryN 的特殊写法 对于简单就直接特殊处理了 不在调用 _curryN 比如 _curry2 里是这样

function _curry2(fn) {
  return function f2(a, b) {
    switch (arguments.length) {
      case 0:
        return f2;
      case 1:
        return _isPlaceholder(a) ? f2
             : _curry1(function(_b) { return fn(a, _b); });
      default:
        return _isPlaceholder(a) && _isPlaceholder(b) ? f2
             : _isPlaceholder(a) ? _curry1(function(_a) { return fn(_a, b); })
             : _isPlaceholder(b) ? _curry1(function(_b) { return fn(a, _b); })
             : fn(a, b);
    }
  };
}

总结下来就是 ramda 中的 curry 函数不仅提供了占位符功能 还能返回中间函数的参数个数
这对于其他依赖 Function.length 的地方提供了很好的支持
不得不说考虑的很细致

#101 解析字串

完成一个 extractStr 函数,可以把一个字符串中所有的 : 到 . 的子串解析出来并且存放到一个数组当中,例如:

extractStr('My name is:Jerry. My age is:12.') // => ['Jerry', '12']

注意,: 和 . 之间不包含 : 和 .。也即是说,如果 ::abc..,则返回 ['abc']。

(本题来源:《JavaScript Cookbook》)

#102 记忆化斐波那契函数(Memoization)

斐波那契数列指的是类似于以下的数列:

1, 1, 2, 3, 5, 8, 13, ....

也就是,第 n 个数由数列的前两个相加而来:f(n) = f(n - 1) + f(n -2)

请你完成 fibonacci 函数,接受 n 作为参数,可以获取数列中第 n 个数,例如:

fibonacci(1) // => 1
fibonacci(2) // => 1
fibonacci(3) // => 2
...

测试程序会从按顺序依次获取斐波那契数列中的数,请注意程序不要超时,也不要添加额外的全局变量。

本题来源:《JavaScript 语言精髓》

#100 把数字转换成中文

完成将 toChineseNum, 可以将数字转换成中文大写的表示,处理到万级别,例如 toChineseNum(12345),返回 一万二千三百四十五。

#94 按下标插入

现在有一个数组存放字符串数据:

['item1', 'item2', 'item3', 'item4', 'item5']
有另外一个数组存放一组对象:

[
  { content: 'section1', index: 0 },
  { content: 'section2', index: 2 }
]

它每个对象表示的是会往原来的数组的 index 坐标插入 content 数据(index 不会重复):

   0      1      2      3      4
 item1  itme2  item3  item4  item5
^             ^ 
|             |

section1 section2

最后结果是:['section1', 'item1', 'item2', 'section2', 'item3', 'item4', 'item5']
请你完成 injectSections 函数,可以达到上述的功能:

injectSections(
['item1', 'item2', 'item3', 'item4', 'item5'],
[
{ content: 'section1', index: 0 },
{ content: 'section2', index: 2 }
]
) // => ['section1', 'item1', 'item2', 'section2', 'item3', 'item4', 'item5']

#91 数组拍平(二)

编写一个 JavaScript generator 函数,接受一个仅包含数字的 多维数组 ,返回一个迭代器,可以遍历得到它拍平以后的结果。例如:

const numbers = flatten2([1, [[2], 3, 4], 5])
numbers.next().value // => 1
numbers.next().value // => 2
numbers.next().value // => 3
numbers.next().value // => 4
numbers.next().value // => 5

照片墙拼接图片

问题

项目有个照片拼接功能 选取几张照片放上照片墙 照片可以拖动旋转缩放 最后生成一张拼接图片保存到服务器

方案1 canvas

image 直接绘制在 canvas 上
canvas 监听事件 然后根据鼠标坐标判断要操作哪个 image 和执行哪种操作
鼠标点击 image 的四个顶点的4个区域触发缩放和旋转 其他位置触发拖动操作
image 每次变形操作时 重绘 canvas
image 在变形时 canvas 会频繁重绘 为了性能一共分了3层 canvas

  1. top 负责监听事件 和绘制当前操作的 image 随鼠标移动事件重绘
  2. middle 绘制其他非操作的 image 只在当前操作 image 改变时才重绘
  3. bottom 绘制照片墙的背景 保持不动

这个方案的难点在于判断当前操作的 image 也就是鼠标点击的位置位于哪个 image 的范围内 对于这种四边形 还是比较好判断的 判断 m 在 tl tr bl br 四个顶点的范围内即可(其实 就是 point in polygon 问题的一个特例 ) 无非在计算4个顶点的时候 要考虑到缩放和旋转 三角函数的知识

最后生成拼接图片时把所有图片都绘制到 top 层 然后生成 base64 发回后端即可

方案2 html2canvas

鼠标事件可以直接绑定到每个 item 上
item 的结构可以这样写

.item
    img
    .tl
    .tr
    .bl
    .br

比方案1优在不用自己计算鼠标位置判断操作元素
item 的旋转缩放移动操作算法和方案1一样 只不过具体实现换成 css 的 transform (要注意transform的先后顺序)

最后调用 html2canvas 把 div 画布 转成 canvas 再得到 base64

其实对于这类简单的 dom 结构 不用 html2canvas 自己遍历 dom 取到 transform 信息 再绘制到 canvas 即可

方案3 svg foreignobject

和方案2类似 只是不用自己遍历 dom 只要把整个 dom 结构塞进 svg 的 里即可
不过最后还是要用canvas 转成 base64

优点就是特别方便
缺点就是 ie 不兼容 foreignobject

function getBase64(img){
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');
    var width = img.width, 
        height = img.height;
     // canvas绘制
     canvas.width = width;
     canvas.height = height;
    // 画布清除
    context.clearRect(0, 0, width, height);    
    // 绘制图片到canvas
    context.drawImage(img, 0, 0);
    
    return canvas.toDataURL();
}

function html2Svg (domStr) {  
            //创建模版字符串
            var svgXML=
            `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
                <foreignObject width="100%" height="100%">${generateXML(html)}</foreignObject>
             </svg>`
            //利用Blob创建svg
            var svg = new Blob([svgXML], {type: 'image/svg+xml'})
            //利用DOMURL.createObjectURL取出对象
            var url = window.URL.createObjectURL(svg);
            var img = new Image()
            img.src = url
            
            return img
        }

// 由于`foreignObject`只能引用XML文档,
// 所以我们需要对DOM进行格式化
function generateXML (domStr) {  
        var doc = document.implementation.createHTMLDocument('');
            doc.write(html);
            doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
            doc = parseStyle(doc)
            console.log(doc)
            html = (new XMLSerializer).serializeToString(doc).replace('<!DOCTYPE html>','');
            return html
}

结论

方案1 的难点在于判断鼠标位置 以及绘制有些繁琐 不过有些 cavans 库 做了封装 比如 createJs 操作起来还算方便
方案3 兼容性一票否决

所以最后还是方案2最好用

php 如何给 form 加 token

#1 手动指定

<form>
<?php echo token(); >
</form>

#2 自动添加

#2.1 自己写正则或解析 html 找到 form 再插入 token

#2.2 output_add_rewrite_var

<?php
output_add_rewrite_var('token','value');

echo '<form><input type="text" /></form>';
//  <form><input type="hidden" name="token" value="value"><input type="text" /></form>

output_add_rewrite_var 可以重写 url 添加 query 但是有有几点需要注意

  1. 只对相对路径生效 http://www.aaa.com/aaa.html 无效
  2. 作为 url query 添加的会按照 rawurlencode 编码
  3. 作为 form hidden 添加的会按照 htmlentites 编码
  4. 能添加的 tag 受 ini 配置 url_rewriter.tags 控制 默认 只有 ‘form=’ 如果想也给 a 标签 添加 需要 赋值为 ’form=,a=href‘
  5. 也可以使用自定义的属性名 比如 <a data-url="aaa.php"> 设置 url_rewriter.tags 为 "a=data-url" 即可 但 一个标签只能有一个 即 href 会失效

参考

从php的缓冲区说起
深入理解php的输出缓冲区

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.