Giter Site home page Giter Site logo

gulp-demos's People

Contributors

wencaistorm avatar wencaizhang avatar

Watchers

 avatar  avatar

gulp-demos's Issues

jQuery 中深浅拷贝的实现

先挖个坑,日后再填 😂

jQuery.extend = jQuery.fn.extend = function() {
    /*
    * 所有的变量声明都放在最上面,会比较清晰
    * arguments: 传入的参数列表,类 Array
    * target: 目标对象,其他对象的成员属性将被附加到该对象上,默认值为传入第一个参数
    * deep: 表示是否进行深拷贝,默认值为 false
    * i: 用来拷贝的对象在参数列表中的索引,默认为 1 (即从第二个参数开始)
    */
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[ 0 ] || {},
        i = 1,
        length = arguments.length,
        deep = false;
 
    // Handle a deep copy situation
    /*
    * 如果传入第一个参数为 boolean 类型,则该参数用来指定是否进行深拷贝
    * 那么,目标对象和用来拷贝的对象按参数顺序往后推一个位置
    * 即将第二个参数指定为 target,同时 i++
    */
    if ( typeof target === "boolean" ) {
        deep = target;
 
        // Skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }
 
    // Handle case when target is a string or something (possible in deep copy)
    /*
    * 如果目标对象不是 Object 类型,将目标对象设为空对象
    * ??? 为什么要判断是否为一个函数
    */
    if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
        target = {};
    }
 
    // Extend jQuery itself if only one argument is passed
    /*
    * 如果没有传入用来拷贝的对象,???
    */
    if ( i === length ) {
        target = this;
        i--;
    }
 
    /*
    * 这里对用来拷贝的对象进行遍历
    * i 已经进行初始化,所以只写一个分号
    */
    for ( ; i < length; i++ ) {
 
        // Only deal with non-null/undefined values
        /*
        * 赋值运算 和 逻辑判断在一条语句中实现
        * 传入的参数不能为 null
        * ??? undefined 呢?
        */
        if ( ( options = arguments[ i ] ) != null ) {
 
            // Extend the base object
            /*
            * 对参数的属性进行遍历
            * src: 目标对象的属性值
            * copy: 拷贝对象的属性值
            */
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];
 
                // Prevent never-ending loop
                /*
                * ???
                */
                if ( target === copy ) {
                    continue;
                }
 
                // Recurse if we're merging plain objects or arrays
                /*
                * 进行递归运算的三个条件
                * 1. 调用时传入的第一个参数为 true
                * 2. 拷贝对象的属性值不为 undefined、null
                * 3. 拷贝对象的属性值为 Object 或者 Array 类型
                */
                if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
                    ( copyIsArray = Array.isArray( copy ) ) ) ) {
 
                    if ( copyIsArray ) {
                    /*
                    * 如果拷贝对象的属性值为 Array 类型,则重置 copyIsArray 为 false,以便下次循环使用
                    * 同时,如果目标对象的同名属性值 src 存在且同为 Array 类型,将 src 赋值给 clone,作为递归调用时的目标对象
                    * 否则赋值为空数组
                    *
                    * 同理,如果拷贝对象的属性值为 Object 类型,做类似的处理
                    */
                        copyIsArray = false;
                        clone = src && Array.isArray( src ) ? src : [];
 
                    } else {
                        clone = src && jQuery.isPlainObject( src ) ? src : {};
                    }
 
                    // Never move original objects, clone them
                    /*
                    * 递归调用需要三个参数,
                    * deep(判断是否深拷贝),来源本次执行时的 deep
                    * clone(目标对象), 来源目标对象属性值
                    * copy(拷贝对象),来源拷贝对象属性值
                    */
                    target[ name ] = jQuery.extend( deep, clone, copy );
 
                // Don't bring in undefined values
                /*
                * 不进行深拷贝,同时属性值不为 undefined,直接将属性值赋值给目标对象的同名属性
                */
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }
 
    // Return the modified object
    return target;
};

underscore 中对象拷贝的实现

_.clone() 代码实现

underscore 中 _.clone() 相关代码如下:

// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
  if (!_.isObject(obj)) return obj;
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

代码很简单:

  1. 通过 _.isObject(obj) 判断参数 obj 是否为引用类型,非引用类型直接返回
  2. 然后通过 _.isArray(obj) 判断参数 obj 是否为数组,如果是数组,返回值为 obj.slice(),否则返回值为 _.extend({}, obj)

类型判断

_.isObject(obj)_.isArray(obj) 的代码如下,以后我们做类型判断的时候也可以借鉴 underscore 的做法

var nativeIsArray = Array.isArray;

// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
  return toString.call(obj) === '[object Array]';
};

// Is a given variable an object?
_.isObject = function(obj) {
  var type = typeof obj;
  return type === 'function' || type === 'object' && !!obj;
};

判断一个变量是否为数组,首先考虑使用 ES5 提供的方法 Array.isArray() ,详细用法可以查看 MDN 中的解释

在不支持 Array.isArray() 的浏览器上,使用 toString.call(obj) === '[object Array]' 的方式来判断,这里留个问题:为什么不用 instanceof 操作符来判断是否为数组呢?

判断一个变量是否为引用数据类型,可以使用 typeof 操作符,但是有两个特殊的变量需要注意:functionnull:

function fn () { console.log( 'hello world' ) };
typeof fn;   // function

typeof null; // object

slice() 方法浅拷贝数组

Array.prototype.slice():
slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。原始数组不会被修改。

slice() 两个参数都省略时,则从索引 0 开始提取至数组末尾:

var arr = [1, 2, [3, 4]];
var newArr = arr.slice();  // [1, 2, [3, 4]]
newArr[2].push(5);

console.log(arr);     // [1, 2, [3, 4, 5]]
console.log(newArr);  // [1, 2, [3, 4, 5]]

_.extend() 实现

这里用到了 _.allKeys(),这个方法返回一个由对象所有的属性名组成的数组:

// Retrieve all the property names of an object.
_.allKeys = function(obj) {
  if (!_.isObject(obj)) return [];
  var keys = [];
  for (var key in obj) keys.push(key);
  // Ahem, IE < 9.
  if (hasEnumBug) collectNonEnumProps(obj, keys);
  return keys;
};

_.extend() 核心代码:

// An internal function for creating assigner functions.
var createAssigner = function(keysFunc, defaults) {
  return function(obj) {
    var length = arguments.length;
    if (defaults) obj = Object(obj);
    if (length < 2 || obj == null) return obj;
    for (var index = 1; index < length; index++) {
      var source = arguments[index],
          keys = keysFunc(source),
          l = keys.length;
      for (var i = 0; i < l; i++) {
        var key = keys[i];
        if (!defaults || obj[key] === void 0) obj[key] = source[key];
      }
    }
    return obj;
  };
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = createAssigner(_.allKeys);

看起来有些复杂,我们可以做一些形式上的改造。

createAssigner() 的返回值是一个方法,另外在 _.extend = createAssigner(_.allKeys); 中,只传入了第一个参数 keysFunc ,没有传入第二个参数 defaults ,即为 undefined

因此,将参数代入之后,可将上面代码改写如下:

_.extend = function(obj) {
  var length = arguments.length;
  if (undefined) obj = Object(obj);  // 可以忽略此行
  if (length < 2 || obj == null) return obj;
  for (var index = 1; index < length; index++) {
    var source = arguments[index],
        keys = _.allKeys(source),
        l = keys.length;
    for (var i = 0; i < l; i++) {
      var key = keys[i];
      if (!undefined || obj[key] === void 0) obj[key] = source[key];
    }
  }
  return obj;
};

分析:

  1. 首先形参和实参的个数可以不相等,上面代码中虽然只有一个参数 obj ,但在实际使用中,可以传入多个参数
  2. 函数内部简单来讲就,首先对传入参数进行遍历(第一个参数除外),在循环体内部再对每个参数的属性进行遍历,将这些属性都拷贝给第一个参数,最后将遍历之后得到的第一个参数返回。
  3. 单就拷贝来说,用法很简单: _.extend({}, obj),即将 obj 的所有属性都复制一份到 {}
  4. 此方法还支持更多参数,例如 _.extend({}, obj1, obj2)。这才是对参数进行遍历的意义所在(集众家之所长),但是要注意参数的先后顺序。如果仅仅是为了拷贝一个对象:_.extend({}, obj) ,直接遍历第二个参数 obj 的属性即可。

最后,貌似一直没有看到深拷贝的方法,emmm

参考资料:

JavaScript 之深浅拷贝

基本类型和引用类型

先来看看 JavaScript 中两种数据类型有何异同。

基本类型和引用类型的保存方式不同。

基本数据类型是按值访问的。基本数据类型的值保存在栈内存中,可以直接对其操作。基本数据类型包含 5 种:UndefinedNullBooleanNumberString

引用类型的值是按引用访问的。引用类型的值保存在堆内存中,另外在栈内存中会有一个引用指针指向堆内存中的对象,变量中保存的实际上是一个指针。在操作对象时,实际上是在操作对象的引用指针而不是实际的对象。

另外,基本类型和引用类型的复制方式也不同。

如果从一个变量向另一个变量复制基本类型的值,会在栈内存中创建一个新值,然后把值复制到新变量的位置上。例如:

var num1 = 5;
var num2 = num1;

现在,将 num1 的值复制给 num2 ,两个变量中的值相同,但是 num2 的值只是 num1 的值的一个副本,因此两者是相互独立的,不会互相影响。

当一个变量向另一个变量复制引用类型的值,同样也会将储存在变量中的值复制一份到为新变量分配的空间中。但是这个值实际上是一个指针,指向储存在堆内存中的一个对象。复制结束后,两个变量存储的指针指向同一个对象,因此改变其中一个变量,就会影响到另一个变量。

var obj1 = { name: 'js' }
var obj2 = obj1;
obj2.name = 'jquery';
console.log( obj1.name ); // jquery

由此可见,基本数据类型的复制非常简单,直接使用赋值运算即可,无副作用。然而引用类型的复制则不能直接使用赋值运算,否则操作的仍是同一个对象,毫无意义。

故下面讨论的拷贝都是针对引用数据类型而言。

浅拷贝 demo

我们说的复制,其实就是希望得到一个和原有对象有相同键值对集合(属性)的新对象,那么我们可以直接遍历对象,将键值对都储存在一个新的对象下,这样新对象和原对象就有一样的键值对集合(属性),也就达到我们的目标。

等一下,这里有个问题,如果某个属性值为引用类型,储存的值为对应的引用指针,则仍然会出现新变量和原有变量相互影响的问题。

以数组为例,若数组中元素都为基本数据类型:

第一步,遍历原数组,单独拷贝其中元素:

var arr = [ 1, 2, 3 ];
var newArr = [];
arr.forEach(function (item, index) {
  newArr[index] = item;
})

console.log(arr);     // [1, 2, 3]
console.log(newArr);  // [1, 2, 3]

第二步,修改新数组:

newArr.push(4);

console.log(arr);     // [1, 2, 3]
console.log(newArr);  // [1, 2, 3, 4]

可以看到,修改 newArr ,原数组 arr 并未发生修改。

同样以数组为例,若数组中存在引用类型的元素:

第一步,遍历进行拷贝

var arr = [ 1, 2, [ 3, 4 ] ];
var newArr = [];
arr.forEach(function (item, index) {
  newArr[index] = item;
})

console.log(arr);     // [1, 2, [3, 4]]
console.log(newArr);  // [1, 2, [3, 4]]

第二步,修改新数组中嵌套的数组

newArr[2].push(5);

console.log(arr);     // [1, 2, [3, 4, 5]]
console.log(newArr);  // [1, 2, [3, 4, 5]]

我们发现,无论是 arr 还是 newArr 都发生了变化。

当属性值为基本数据类型时,我们拷贝出来的新对象和原对象互不影响,当属性值为引用类型时,新对象和原对象在修改引用类型的属性值时相互影响

所以说这种拷贝方式不够彻底,即浅拷贝。

总结浅拷贝的方法

现在我们封装一个同时适用于 ArrayObject 类型的浅拷贝的方法:

function shallowCopy (obj) {
  if (typeof obj !== 'object') return obj;

  var newObj = obj instanceof Array ? [] : {};

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

  return newObj;
}

注意几点:

  1. 只拷贝引用类型,基本数据类型直接返回即可
  2. 根据 obj 的类型来判断新建一个空数组或者空对象
  3. 遍历时,使用 hasOwnProperty() 过滤出对象实例的属性

总结深拷贝的方法

上面演示了浅拷贝,浅拷贝存在的问题是引用类型的属性也是引用类型,但其实最终引用类型的键值也是由基本类型组成的。

var person = { name: 'jake' }person 是引用类型,但是 person 的属性名和属性值都是基本类型。

所以如果我们对其进行递归浅拷贝,总会拷贝到键值均为基本数据类型的那一步。

具体代码如下:

function deepCopy (obj) {
	if ( typeof obj !== 'object' ) return obj;

	var newObj = obj instanceof Array ? [] : {};

	for ( var key in obj ) {
		if ( obj.hasOwnProperty( key ) ) {
			newObj[key] = typeof obj[key] === 'object' ? deepCopy( obj[key] ) : obj[key];
		}
	}

	return newObj;
}

现在我们来测试一下:

var arr = [ 1, 2, [ 3, 4 ] ];
var newArr = deepCopy(arr);

newArr[2].push(5);

console.log( arr[2] );     // [3, 4]
console.log( newArr[2] );  // [3, 4, 5]

此时尽管 arr 中有引用类型的元素,通过深拷贝(递归浅拷贝)得到新对象 newArr 之后,修改其引用类型的属性可以发现,arrnewArr 不再互相影响,所以这种递归拷贝的方式叫做深拷贝。

封装深浅拷贝的方法

上面分别总结了浅拷贝和深拷贝的方法,但是这两个方法的功能以及代码都有很大的相似度,我们可以考虑将它们封装成一个方法,通过多传入一个参数区分深拷贝和浅拷贝,如:extend( [deep], obj )
大致思路如下:

  1. 第一个参数 deep 为布尔类型,区分深浅拷贝,可忽略,忽略时视为浅拷贝(即默认值 false
  2. 如果传入参数 deep 那么第二个参数是拷贝的目标对象,如果忽略 deep 参数,那么第一个参数就是拷贝的目标对象。
  3. 在为新建空对象的拷贝属性时,通过 deep 判断是否进行深拷贝。

代码如下:

function extend() {
	var deep = false;
	var target = arguments[0];
	var newObj, copy;
	if ( typeof target == 'boolean' ) {
		deep = target;
		target = arguments[1];
	}
	newObj = target instanceof Array ? [] : {};
	
	if ( typeof target !== 'object' ) return target;
	
	for ( var key in target ) {
		if ( target.hasOwnProperty( key ) ) {
			copy = target[ key ];
			newObj[key] = deep && typeof copy === 'object' ? extend(deep, copy) : copy;
		}
	}

	return newObj;
}

以数组为例测试一下:

var arr = [ 1, 2, [ 3, 4 ] ];
var newArr = extend(false, arr);
var newArr2 = extend(true, arr);

newArr[2].push(5);

console.log( arr[2] );     // [1, 2, [3, 4, 5]]
console.log( newArr[2] );  // [1, 2, [3, 4, 5]]
console.log( newArr2[2] ); // [1, 2, [3, 4]]

和预期一样,Good Job!

参考资料:

LAMP-Install


title: Ubuntu 安装 LAMP 环境
tags:

  • 部署环境
  • Linux
    abbrlink: 5ba4e681
    date: 2017-11-27 21:22:53
    categories:

[toc]

安装开发环境亦或部署环境,向来不是一件容易的事情。

写在前面

  1. 安装顺序,一定是先安装 Apache,后安装 php,因为 php 安装完成之后,会自动修改 Apache 的配置文件
  2. 关于 Apache 配置文件,有的地方叫 httpd.conf ,其实就是 apache2.conf

mysql 安装

  • 安装命令:
apt-get install mysql-server
apt-get install mysql-client 
apt-get install libmysqlclient-dev
  • 安装过程中,会提示为 root 用户设置密码
  • 安装完成之后:
    • 连接 mysql:mysql -u roo t -p
    • 退出 mysql:exit

apache 安装

  • 安装命令
apt-get install apache2
  • 安装完成之后,apache2 的默认主目录:/var/www/
    apache2 的操作命令(启动 Apache 之后,可以在浏览器中打开 localhost 地址查看 Apache 的默认页面):
    • 启动:/etc/init.d/apache2 start
    • 重启:/etc/init.d/apache2 restart
    • 关闭:/etc/init.d/apache2 stop

php 安装

  • 安装命令
apt-get install php
  • 安装完成之后
    查看版本信息:php -v

‘test’

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.