js立即执行函数表达式(IIFE)

有时需要在定义函数之后,立即调用该函数。这种函数就叫做立即执行函数,全称为立即调用的函数表达式IIFE(Imdiately Invoked Function Expression)。

 [注意]javascript引擎规定,如果function关键字出现在行首,一律解释成函数声明语句。

//声明语句(必须有函数名)
function foo(){}

所以,解决方法就是不要让function出现在行首,让引擎将其理解成一个表达式

最常用的两种写法:

(function(){ /* code */ }()); 
(function(){ /* code */ })(); 

其他写法:

!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();

var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();

css实现border的0.5px

用css实现border的0.5px,有以下几种方式:

1.使用png图片,设置元素的background-image和background-size两个属性来实现。
2.使用png图片,设置border-image来实现。
3.在meta元素的viewport标签中,设置scale属性(h5.m.taobao.com使用该方法,要设置成手机模式才可以)。

但是使用这几种方式,各有各的劣势。

1.使用png的图片,不管是设置在background还是设置在border中,都有一个问题就是,这个元素没有办法设置圆角,当如果我需要改变这个边框的颜色时,需要重新设计图片,需求变更时不灵活。
2.在meta中设置scale属性,那么接下来的整个页面的实现,就变的更加困难,因为所有的地方,都要考虑到宽高,大小等,都会被scale属性影响的。

因此,在推荐用一下做法【transform:scale()方法】来实现0.5px:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
    .custom-border{
        float: left;
        margin-right: 10px;
        width:200px;
        height:100px;
        border:1px solid #333;
        background-color:#eee;
        padding:10px;
    }
    .scale-border{
        float: left;
        height:100px;
        position:relative;
        padding:10px;
        width: 200px;
    }
    .border{
        -webkit-transform:scale(0.5);
        transform:scale(0.5);
        position:absolute;
        border:1px solid #333;
        top:-50%;
        right:-50%;
        bottom:-50%;
        left:-50%;
        border-radius: 10px;
        background-color:#eee;
    }
    .content{
        position:relative;
        z-index:2;
    }
    </style>
</head>
<body>
    <div class="custom-border">边框宽度1px</div>
    <div class="scale-border ">
        <div class="content">边框宽度0.5px</div>
        <div class="border"></div>
    </div>
</body>
</html>

如下图所示:

js的引用、深拷贝与浅拷贝

这篇文章主要总结下什么是js的引用、深拷贝与浅拷贝。

要先理解什么是深拷贝和浅拷贝,就要先回顾一下js的基础知识。

js的基本数据类型与引用类型(栈与堆)

什么是栈与堆

栈(stack)是自动分配的内存空间,由系统自动释放;堆(heap)则是动态分配内存,大小不定也不会自动释放。

基本数据类型与引用类型

js的基本数据类型:number、string、boolean、null与undefined,他们存放在栈中,JS基本数据类型的变量存放的是基本类型数据的实际值,而不是指针。
这里顺便讲下,存放在栈中的值(即js的基本数据类型)是不可改变的

var a = '123'
a[0] = 2
console.log(a) // '123'

引用类型:对象(object)、数组(array)、函数(function),他们存放在堆中,引用数据类型的变量保存对它的引用,即指针。
举个例子

var a = [1,2,3]
var b = a
console.log(b) // [1,2,3]
b[0] = 2
console.log(a) // [2,2,3]
console.log(b) // [2,2,3]

可以看出,把数组a赋值给b,改变数组b的值,则a中的值也跟随改变。由此可以看出把a赋值给b,只是把a指向的指针赋值给了b,而并没有把值赋值给b。这就是js的引用。

对于上面这种情况,如果我们想要一组和a一样的新数组,并且修改b并不影响a,那该怎么办呢?
这时就可以用浅拷贝
var a = [1,2,3]
var b = []
for(var i=0;i<a.length;i++)
{
    b.push(a[i])
}
console.log(b) // [1,2,3]
b[0] = 2
console.log(a) // [1,2,3]
console.log(b) // [2,2,3]

//特别说明,由于引用类型比较是传地址,而这是a和b不在同一个地址中,所以 a!==b
a === b //false

完美,我们得到了一个不改变原始变量的新数组!到这里,那深拷贝又有什么用呢?不急,往下看~

js的深拷贝

有一些时候引用类型不想上面那个简单==,比如

var obj = {
    name: 'zjz',
    age: 24,
    likeNumber: [1,2,3]
}

如果用浅拷贝是这么写:

var newObj = {}
for(var prop in obj){
    if(obj.hasOwnProperty(prop)){
        newObj[prop] = obj[prop]
    }
}

这时我们得到一个新的对象newObj,

console.log(newObj) 
/* {
    name: 'zjz',
    age: 24,
    likeNumber: [1,2,3]
}
*/

这时改变newObj中的值

newObj.name = 'zjz1'
newObj.likeNumber[0] = '5'

console.log(obj)
/* {
    name: 'zjz',
    age: 24,
    likeNumber: [1,2,3]
}
*/
console.log(newObj)
/* {
    name: 'zjz1',
    age: 24,
    likeNumber: [5,2,3]
}
*/

可以看出,改变newObj的值,name属性在obj中确实没变,但是likeNumber却变了,这是怎么回事呢?

这是因为浅拷贝值赋值了一层对象的属性,并不包括对象里的引用类型数据,所以就会出现上面的改变newObj中的likeNumber而obj中的likeNumber跟随变得事情。

这种情况要怎么办呢?是时候让深拷贝登场啦\^o^/

什么是深拷贝?

深拷贝就是对对象以及对象的所有子对象进行拷贝,实现方法:

    var obj = {
    name: 'zjz',
    age: 24,
    likeNumber: [1,2,3]
}
var obj1 = {}
//进行深拷贝操作的函数
function inCopy(obj1,obj2) {
    var obj1 = obj1 || {};//容错处理
    for (var k in obj2) { 
        if(obj2.hasOwnProperty(k)){ //只拷贝实例属性,不进行原型的拷贝
            if(typeof obj2[k] == 'object') { //引用类型的数据单独处理
                obj1[k] = Array.isArray(obj2[k])?[]:{};
                inCopy(obj1[k],obj2[k]); //递归处理引用类型数据
            }else{
                obj1[k] = obj2[k]; //值类型的数据直接进行拷贝
            }
        }
    }
}

inCopy(obj1,obj)
obj1.likeNumber[0] = 5
console.log(obj)
/*
{
    name: 'zjz',
    age: 24,
    likeNumber: [1,2,3]
}
*/
console.log(obj1)
    /*
{
    name: 'zjz',
    age: 24,
    likeNumber: [5,2,3]
}
*/

js函数柯里化

柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。

柯里化其实本身是固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求。这增加了函数的适用性,但同时也降低了函数的适用范围。

那么柯里话的好处呢?有以下三个方面:
柯里化有3个常见作用:1. 参数复用;2. 提前返回;3. 延迟计算/运行

通俗的说,就是一个函数接收一个参数,然后在return另一个函数接收另一个参数,如果参数长度任意,则map/reduce或者其他手段循环出来。

例如,一个确定只有两个参数的柯里化

var add = function(x) {
  return function(y) {
    return x + y;
  };
};

var increment = add(1);
var addTen = add(10);

increment(2);
// 3

addTen(2);
// 12

这是add()中的形参x表示接收到的第一个参数,也就是var increment = add(1) 中的1和 var addTen = add(10) 中的10,increment(2)addTen(2)中的2分别由y接收到。

那么参数长度不确定呢?

var currying = function (fn) {
    var _args = [];
    return function () {
        if (arguments.length === 0) {
            return fn.apply(this, _args);
        }
        Array.prototype.push.apply(_args, [].slice.call(arguments));
        return arguments.callee;
    }
};

var multi=function () {
    var total = 0;
    for (var i = 0, c; c = arguments[i++];) {
        total += c;
    }
    return total;
};

var sum = currying(multi);  

sum(100,200)(300);
sum(400);
console.log(sum());     // 1000  (空白调用时才真正计算)

js闭包

js闭包,在我的理解来说主要就是在函数外部读取函数内部的变量。
由于js的‘链式作用域’,函数内部的值无法被外部读取。可是如果在某些情况下如果想在函数外部的值读取函数内部的值怎么办呢?这是就要用到闭包了。

function a() {
    var n = 666;
    function b() {
        console.log(n)
    }
    return b();
}
var c = a();
c();

可以在console中看到打印出的666

这个是什么原理呢?

在函数a中又声明了一个函数b,那么照js的‘链式作用域’的规则就可以得出函数b可以读到函数a的内部的值。这是,在函数a中把函数b作为返回值返回出来,就可以在函数a的外部读取函数b,而函数b又可以读取到函数a中的值,因此,用这种方法就可以在函数a外部读取到函数b的值了。

但是要特别注意的是: 因为函数b被赋值给了一个全局变量,因此函数b会一直待在内存中,而函数a又是函数b的复函数,因此函数a也一直待在内存中。

使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

css之制作三角形

箭头向上三角形

#triangle-up {
    width: 0;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 100px solid red;
}

箭头向下三角形

#triangle-down {
    width: 0;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-top: 100px solid red;
}

箭头向左三角形

#triangle-left {
    width: 0;
    height: 0;
    border-top: 50px solid transparent;
    border-right: 100px solid red;
    border-bottom: 50px solid transparent;
}

箭头向右三角形

#triangle-right {
    width: 0;
    height: 0;
    border-top: 50px solid transparent;
    border-left: 100px solid red;
    border-bottom: 50px solid transparent;
}

左上角三角形

#triangle-topleft {
    width: 0;
    height: 0;
    border-top: 100px solid red;
    border-right: 100px solid transparent;
}

右上角三角形

#triangle-topright {
    width: 0;
    height: 0;
    border-top: 100px solid red;
    border-left: 100px solid transparent; 
}

左下角三角形

#triangle-bottomleft {
    width: 0;
    height: 0;
    border-bottom: 100px solid red;
    border-right: 100px solid transparent;
}

右下角三角形

#triangle-bottomright {
    width: 0;
    height: 0;
    border-bottom: 100px solid red;
    border-left: 100px solid transparent;
}

AMD,CMD,commonJS,UMD和es6 module的概念与区别

Javascript模块化

模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在。对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得“有理可循”。

首先,既然是模块化设计,那么作为一个模块化系统所必须的能力:

  1. 定义封装的模块。
  2. 定义新模块对其他模块的依赖。
  3. 可对其他模块的引入支持。

1.CommonJS和Node

CommonJS是服务器端模块的规范,Node.js采用了这个规范

CommonJS规范特点:
  1. CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作,模块加载的顺序按照其在代码中出现的顺序加载。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。
  2. 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。(输入的是被输出值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值了)
  3. 一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的。
    使用方式:

根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。commonjs规定,每个模块内部,module变量代表当前模块,这个变量是一个对象,它的exports 属性(即module.exports)是对外的接口,加载某个模块,其实是加载该模块的module.exports属性

// example.js
let x = 5;
let addX = function(value){
    return value + x;
};

module.exports.x = x;
module.exports.addX = addX;

在上面代码通过 module.exports输出 变量x 和 函数 addX.

require 方法用于加载模块

var example = require('./example.js')';
console.log(example.x);
console.log(example.addX(1);

为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。

var exports = module.exports;
于是我们可以直接在 exports 对象上添加方法,表示对外输出的接口,如同在module.exports上添加一样。注意,不能直接将exports变量指向一个值,因为这样等于切断了exports与module.exports的联系。
提出问题

但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 等解决方案。 那为什么在浏览器环境,就必须采用异步模式加载模块?

2.AMD规范和RequireJs

AMD 全称是 Asynchronous Module Definition ,即异步模块加载机制

RequireJS 是一个前端的模块化管理的工具库,遵循AMD规范,它的作者就是AMD规范的创始人 James Burke。
RequireJS 的基本思想为:通过一个函数来将所有所需要的或者说所依赖的模块实现装载进来,然后返回一个新的函数(模块),我们所有的关于新模块的业务代码都在这个函数内部操作,其内部也可无限制的使用已经加载进来的模块。
RequireJs特点:

  1. AMD规范是预加载,也就是说会将所有模块全加载,也可以实现动态加载(requirejs2.0 版本也支持)。
  2. 管理模块之间的依赖性,便于代码的编写和维护,可以让代码松耦合

RequireJs的用法

<script src="scripts/require.js"></script>
<script src="scripts/main.js"></script>

那么scripts下的main.js则是指定的主代码脚本文件,所有的依赖模块代码文件都将从该文件开始异步加载进入执行。

define用于定义模块,RequireJS要求每个模块均放在独立的文件之中。按照是否有依赖其他模块的情况分为独立模块和非独立模块。

独立模块,不依赖其他模块。直接定义:

define({
    method1: function(){},
    method2: function(){}
});
// 也等价于
define(function(){
    return {
        method1: function(){},
        method2: function(){}
    }
});

非独立模块,对其他模块有依赖。

define([ 'module1', 'module2' ], function(m1, m2) {
    ...
});
// 或者是
define(function(require) {
    var m1 = require('module1'),
        m2 = require('module2');
    ...
});

require方法调用模块
在require进行调用模块时,其参数与define类似。

require(['foo', 'bar'], function(foo, bar) {
    foo.func();
    bar.func();
} );

define 和 require 这两个定义模块,调用模块的方法合称为AMD模式,定义模块清晰,不会污染全局变量,清楚的显示依赖关系。AMD模式可以用于浏览器环境并且允许非同步加载模块,也可以按需动态加载模块。

3.CMD和SeaJS

CMD是SeaJS 在推广过程中对模块定义的规范化产出
Common Module Definition表示通用模块定义,该规范是国内发展出来的,由阿里的玉伯提出。

SeaJS特点:

  1. CMD规范是懒加载,按需加载,也就是在require的时候相应的模块才会被加载进来。
  2. 对于依赖的模块CMD是延迟执行。
  3. AMD的API默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹

    //AMD
    define([‘./a’,’./b’], function (a, b) {

    //依赖一开始就写好
    a.test();
    b.test();
    

    });

    //CMD
    define(function (requie, exports, module) {

    //依赖可以就近书写
    var a = require('./a');
    a.test();
    
    ...
    //软依赖, 动态加载
    if (status) {
    
        var b = requie('./b');
        b.test();
    }
    

    });

seajs 使用demo https://www.cnblogs.com/imwtr/p/4666150.html
https://www.jd.com/ js main.min.js 文件用的就是seaJs

4.UMD

UMD是AMD和CommonJS的糅合

AMD模块以浏览器第一的原则发展,异步加载模块。

CommonJS模块以服务器第一原则发展,选择同步加载,它的模块无需包装。
这迫使人们又想出另一个更通用的模式UMD (Universal Module Definition)。希望解决跨平台的解决方案。
UMD先判断是否支持Node.js的模块(exports)是否存在,存在则使用Node.js模块模式。
在判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。

(function (window, factory) {
    if (typeof exports === 'object') {

        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {

        define(factory);
    } else {

        window.eventUtil = factory();
    }
})(this, function () {
    // module ...
});

node 包 moment.js就是可以跨平台使用的,内部采用的就是这种写法

5.ES6 Module模块化

ES6 的模块自动采用严格模式,不管你有没有在模块头部加上”use strict”;。

模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。

Module特点:
  1. ES6模块中的值属于【动态只读引用】。
  2. 对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
  3. 对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
  4. 循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。
  5. 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
  6. 模块脚本自动采用严格模式,不管有没有声明use strict。
  7. 模块之中,可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export命令输出对外接口。
  8. 模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字,是无意义的。
  9. 同一个模块如果加载多次,将只执行一次。

    export var firstName = ‘Michael’;
    export var lastName = ‘Jackson’;
    export var year = 1958;

export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

// main.js
import {firstName, lastName, year} from './profile.js';

function setName(element) {
element.textContent = firstName + ' ' + lastName;
}

ionic在ios端的打包与上传到app store

搞了一段时间的ionic在ios端的打包上传,终于弄好了,期间有遇到很多坑,先记录下来,以供以后用到的人学习参考。

ionic打包成ipa并上传到app store,第一反应是怎么打包,和打包原生app有什么不同。其实没什么不同,完全可以看作是一个原生app的打包发布,网上打包原生app的教程也同样适用于ionic的打包发布。

可以参考一下内容打包发布

Xcode打包项目(.xcodeproj=>.ipa)
iOS开发之2016年App提交上架App Store最新流程(iPhone)

下面说说这两篇文章没有提到我却遇到的坑。。TT

1.按照上面的步骤,得到的证书确是 ‘不受信任/由未知机构颁发的’ ,在打包的时候会报错,Missing iOS Distribution signing identity …
网上说是删除过期证书,但是我删完了也没用,还是报错,看到了下面这片文章

Missing iOS Distribution signing identity问题解决

因该是AppleWWDRCA证书无效(但我这没过期。。所以具体原因不太清楚),下载一个新的,双击安装就好了。

下载地址

  1. 打包时候在General下的Bundle Identifier的名字要与申请的保持一致

  2. 在上传的时候,要General下的Deployment Info 下的Devices,如果只要选择iPhone,则选择iPhone,想全选选择Universal。

  3. 上传说明图时,上传一下规格

4.7寸,5.5寸,ipad五种图片,对应尺寸大小:
3.5寸:横坚屏 640960 或960640
4寸:横坚屏 6401036 或1036640
4.7寸:横坚屏 7501334 或1334750
5.5寸:横坚屏 12422208 或22081242
ipad:横坚屏 1024768 或7681024

(5.5寸测试成功)

  1. icon图标上传大小为1024*1024的正方形

额,目前遇到想到就这么多,望对读者有帮助。