转到正文

浪淘沙

静观己心,厚积薄发

今天面试被问到一个问题。问题是这样的:“一个数组里面有正整数和负整数,对这个数组进行排序,要求,距离100比较近的排在前面。比如102距离100为2,90距离100为10,所以102排在90前面。”

这个算法很简单,下面直接给出我的程序:


function cmp($a, $b)
{
    $ta = abs(100-$a);
    $tb = abs(100-$b);
    if ($ta == $tb) {
        return 0;
    }
    return ($ta < $tb) ? -1 : 1;
}

$origin = array(
    100,0,-10,-20,-30,10,30,50,80,90,102,98
);

usort($origin, "cmp");

foreach ($origin as $key => $value) {
    echo "{$key}=>{$value}<br/>";
}

打印结果如下:


0=>100
1=>102
2=>98
3=>90
4=>120
5=>80
6=>130
7=>50
8=>30
9=>10
10=>0
11=>-10
12=>-20
13=>-30

阅读全文

特别喜欢PHP自带的自然排序 natsort / natcasesort。为此,还专门看了php 源码,详情请见传送门

这两天,不是太忙,就根据自己意愿用php重新实现了下,并稍微做了点修改。代码如下:

<?php

/**
 * @param $target
 * @param $destination
 *
 * @return int
 */
function strnatcmpExFoldCase0Improve($target, $destination)
{
    $return = natSortExtend($target, count($target), $destination, count($destination), 0);
    return $return;
}

function strnatcmpExFoldCase1Improve($target, $destination)
{
    return natSortExtend($target, count($target), $destination, count($destination), 1);
}

function natSortExtend($target, $targetLen, $destination, $destinationLen, $foldCase)
{
    $aend               = $targetLen;
    $bend               = $destinationLen;
    $targetLeading      = 1;//最后一次是字母或者字母后面紧跟了n个数字'0',可以跳过后面紧跟的0
    $destinationLeading = 1;//最后一次是字母或者字母后面紧跟了n个数字'0',可以跳过后面紧跟的0

    if ($targetLen == 0 || $destinationLen == 0) {
        return ($targetLen == $destinationLen ? 0 : ($targetLen > $destinationLen ? 1 : -1));
    }

    $targetIndex      = 0;
    $destinationIndex = 0;
    while (1) {
        $currentTargetItem      = $target[$targetIndex];
        $currentDestinationItem = $destination[$destinationIndex];

        /* skip over leading zeros */
        while ($targetLeading && $currentTargetItem == '0' && ($targetIndex + 1 < $aend) && ctype_digit(
                        $target[$targetIndex + 1]
                )) {
            $currentTargetItem = $target[++$targetIndex];
        }

        while ($destinationLeading && $currentDestinationItem == '0' && ($destinationIndex + 1 < $bend) && ctype_digit(
                        $destination[$destinationIndex + 1]
                )) {
            $currentDestinationItem = $destination[++$destinationIndex];
        }

        $targetLeading      = 0;
        $destinationLeading = 0;

        /* Skip consecutive whitespace */
        while (ctype_space($currentTargetItem)) {
            $currentTargetItem = $target[++$targetIndex];
        }

        while (ctype_space($currentDestinationItem)) {
            $currentDestinationItem = $destination[++$destinationIndex];
        }

        /* process run of digits */
        if (ctype_digit($currentTargetItem) && ctype_digit($currentDestinationItem)) {
            $fractional = ($currentTargetItem == '0' || $currentDestinationItem == '0');

            $ap = array_slice($target, $targetIndex);
            $bp = array_slice($destination, $destinationIndex);

            if ($fractional) {
                $result = compare_left($ap, count($ap), $bp, count($bp));
            } else {
                $result = compare_right($ap, count($ap), $bp, count($bp));
            }
            if ($result != 0) {
                return $result;
            } else if ($targetIndex == $aend && $destinationIndex == $bend) {
                /* End of the strings. Let caller sort them out. */
                return 0;
            } else {
                /* Keep on comparing from the current point. */
                $currentTargetItem      = $target[$targetIndex];
                $currentDestinationItem = $destination[$destinationIndex];
            }
        }

        if ($foldCase) {
            $currentTargetItem      = strtoupper($currentTargetItem);
            $currentDestinationItem = strtoupper($currentDestinationItem);
        }

        if ($currentTargetItem < $currentDestinationItem) {
            return -1;
        } else if ($currentTargetItem > $currentDestinationItem) {
            return +1;
        }

        $targetIndex++;
        $destinationIndex++;
        if ($targetIndex >= $aend && $destinationIndex >= $bend) {
            /* The strings compare the same.  Perhaps the caller
                will want to call strcmp to break the tie. */
            if ($aend < $bend) {
                return -1;
            } else if ($aend > $bend) {
                return 1;
            } else {
                return 0;
            }
        } else if ($targetIndex >= $aend) {
            return -1;
        } else if ($destinationIndex >= $bend) {
            return 1;
        }

        if (ctype_alpha($currentTargetItem)) {//需要处理后面的0 ,空格可以跳过
            $targetLeading = 1;
        } else if (ctype_digit($currentTargetItem) && $currentTargetItem != '0') {//非0数字 后面的0不可以跳过
            $targetLeading = 0;
        }
        if (ctype_alpha($currentDestinationItem)) {//需要处理后面的0 ,空格可以跳过
            $destinationLeading = 1;
        } else if (ctype_digit($currentDestinationItem) && $currentDestinationItem != '0') {//非0数字 后面的0不可以跳过
            $destinationLeading = 0;
        }
    }

    return 0;
}

function compare_left($a, $aend, $b, $bend)
{
    $aIndex = 0;
    $bIndex = 0;
    /* Compare two left-aligned numbers: the first to have a
       different value wins. */
    for (; ; $aIndex++, $bIndex++) {
        if ($aIndex >= count($a)) {
            echo 'warning $aIndex: ', $aIndex, ' => ', var_export($a);
            $ca = 0;
        } else {
            $ca = $a[$aIndex];//warning..
        }

        if ($bIndex >= count($b)) {
            echo 'warning $bIndex:', $bIndex, ' => ', var_export($b);
            $cb = 0;
        } else {
            $cb = $b[$bIndex];//warning..
        }

        if (($aIndex == $aend - 1 || !ctype_digit($ca)) && ($bIndex == $bend - 1 || !ctype_digit($cb))) {
            return 0;
        } else if ($aIndex == $aend - 1 || !ctype_digit($ca)) {
            return -1;
        } else if ($bIndex == $bend - 1 || !ctype_digit($cb)) {
            return +1;
        } else if ($ca < $cb) {
            return -1;
        } else if ($ca > $cb) {
            return +1;
        }
    }

    return 0;
}

function compare_right($a, $aend, $b, $bend)
{
    $aIndex = 0;
    $bIndex = 0;
    $bias   = 0;

    /* The longest run of digits wins.  That aside, the greatest
       value wins, but we can't know that it will until we've scanned
       both numbers to know that they have the same magnitude, so we
       remember it in BIAS. */
    for (; ; $aIndex++, $bIndex++) {
        if ($aIndex >= count($a)) {
            echo 'warning $aIndex: ', $aIndex, ' => ', var_export($a);
            $ca = 0;
        } else {
            $ca = $a[$aIndex];//warning..
        }

        if ($bIndex >= count($b)) {
            echo 'warning $bIndex:', $bIndex, ' => ', var_export($b);
            $cb = 0;
        } else {
            $cb = $b[$bIndex];//warning..
        }
        if (($aIndex == $aend - 1 || !ctype_digit($ca)) && ($bIndex == $bend - 1 || !ctype_digit($cb))) {
            return $bias;
        } else if ($aIndex == $aend - 1 || !ctype_digit($ca)) {
            return -1;
        } else if ($bIndex == $bend - 1 || !ctype_digit($cb)) {
            return +1;
        } else if ($ca < $cb) {
            if (!$bias) {
                $bias = -1;
            }
        } else if ($ca > $cb) {
            if (!$bias) {
                $bias = +1;
            }
        }
    }

    return 0;
}

$data = array(
        '1ab' => 'abc001',
        '2ab' => 'abc021',
        '3ab' => 'abc1',
        '4ab' => 'abc2',
        '5ab' => 'abc4',
        '6ab' => 'abc50',
        '1ae' => 'aec001',
        '2ae' => 'aec021',
        '3ae' => 'aec1',
        '4ae' => 'aec2',
        '5ae' => 'aec4',
        '6ae' => 'aec50',
        '1be' => 'bec001',
        '2be' => 'bec021',
        '3be' => 'bec1',
);

function prestrnatcmpEx2($data, $fold_case)
{
    foreach ($data as $index => $item) {
        $data[$index] = str_split($item);
    }

    if ($fold_case == 0) {
        usort($data, 'strnatcmpExFoldCase0Improve');
    } else {
        usort($data, 'strnatcmpExFoldCase1Improve');

    }

    foreach ($data as $index => $item) {
        $data[$index] = implode('', $item);
    }

    echo '<pre>';
    var_export($data);
    echo '</pre>';
}

prestrnatcmpEx2($data, 0);

$data = array(
        '1ab' => 'abc001',
        '2ab' => 'abc021',
        '3ab' => 'abc1',
        '4ab' => 'abc2',
        '5ab' => 'abc4',
        '6ab' => 'abc50',
        '1ae' => 'aec001',
        '2ae' => 'aec021',
        '3ae' => 'aec1',
        '4ae' => 'aec2',
        '5ae' => 'aec4',
        '6ae' => 'aec50',
        '1be' => 'bec001',
        '2be' => 'bec021',
        '3be' => 'bec1',
);

natsort($data);
echo '<pre>';
var_export($data);
echo '</pre>';


//输出结果
array (
  0 => 'abc1',
  1 => 'abc001',
  2 => 'abc2',
  3 => 'abc4',
  4 => 'abc021',
  5 => 'abc50',
  6 => 'aec1',
  7 => 'aec001',
  8 => 'aec2',
  9 => 'aec4',
  10 => 'aec021',
  11 => 'aec50',
  12 => 'bec1',
  13 => 'bec001',
  14 => 'bec021',
)
array (
  '1ab' => 'abc001',
  '2ab' => 'abc021',
  '3ab' => 'abc1',
  '4ab' => 'abc2',
  '5ab' => 'abc4',
  '6ab' => 'abc50',
  '1ae' => 'aec001',
  '2ae' => 'aec021',
  '3ae' => 'aec1',
  '4ae' => 'aec2',
  '5ae' => 'aec4',
  '6ae' => 'aec50',
  '1be' => 'bec001',
  '2be' => 'bec021',
  '3be' => 'bec1',
)
阅读全文

natsort的核心实现方法如下:

/* {{{ strnatcmp_ex
*/
PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, int fold_case)
{
    unsigned char ca, cb;   
    char const *ap, *bp;
    char const *aend = a + a_len,
    *bend = b + b_len;
    int fractional, result;
    short leading = 1;

    if (a_len == 0 || b_len == 0) {
        return (a_len == b_len ? 0 : (a_len > b_len ? 1 : -1));
    }  

    ap = a;
    bp = b;
    while (1) {
        ca = *ap; cb = *bp;

        /* skip over leading zeros */
        while (leading && ca == '0' && (ap+1 < aend) && isdigit((int)(unsigned char)*(ap+1))) {
            ca = *++ap;
        }

        while (leading && cb == '0' && (bp+1 < bend) && isdigit((int)(unsigned char)*(bp+1))) {
            cb = *++bp;
        }

        leading = 0;

        /* Skip consecutive whitespace */
        while (isspace((int)(unsigned char)ca)) {
            ca = *++ap;
        }

        while (isspace((int)(unsigned char)cb)) {
            cb = *++bp;
        }

        /* process run of digits */
        if (isdigit((int)(unsigned char)ca)  &&  isdigit((int)(unsigned char)cb)) {
            fractional = (ca == '0' || cb == '0');

            if (fractional)
                result = compare_left(&ap, aend, &bp, bend);
            else
                result = compare_right(&ap, aend, &bp, bend);

            if (result != 0)
                return result;
            else if (ap == aend && bp == bend)
                /* End of the strings. Let caller sort them out. */
                return 0;
            else {
                /* Keep on comparing from the current point. */
                ca = *ap; cb = *bp;
            }
        }

        if (fold_case) {
            ca = toupper((int)(unsigned char)ca);
            cb = toupper((int)(unsigned char)cb);
        }

        if (ca < cb)
            return -1;
        else if (ca > cb)
            return +1;

        ++ap; ++bp;
        if (ap >= aend && bp >= bend)
            /* The strings compare the same.  Perhaps the caller
                will want to call strcmp to break the tie. */
            return 0;
        else if (ap >= aend)
            return -1;
        else if (bp >= bend)
            return 1;
    }
}
/* }}} */
阅读全文

fork 直接操作步骤为

<br />
	git clone https://github.com/YOUR-USERNAME/PROJECT_NAME_WHICH_YOU_WANNA<br />
	git remote add upstream PROJECT_WHICH_YOU_WANNA_GIT_URI<br />
	

syncing 直接操作步骤为:

<br />
	git fetch upstream<br />
	git checkout master # 如果本地没有co 才需要执行该操作<br />
	git merge upstream/master<br />
	

详见

1、Fork A Repo

2、Syncing a fork

阅读全文

hset返回结果:

  • 1 if field is a new field in the hash and value was set.
  • 0 if field already exists in the hash and the value was updated.

一般情况下返回值的意思为:1 成功,0:失败。但是 这hset比较特殊。0表示原来已经存在的值被更新成功。至于操作失败的情况,redis被没有给一个结果。所以,妄想通过hset的返回值0,1来判断操作是否成功,是行不通的。

PS:详情请移步:http://redis.io/commands/hset

阅读全文

gettype获取变量的类型

string gettype ( mixed $var )  返回 PHP 变量的类型 var .

PHP manual 有个大大的warning:

不要使用 gettype() 来测试某种类型,因为其返回的字符串在未来的版本中可能需要改变。此外,由于包含了字符串的比较,它的运行也是较慢的。

使用 is_* 函数代替。

但是有时候 我们又不得不使用gettype这样的功能来判断参数的类型,这个时候我们应该怎么办?O(∩_∩)O哈哈~ 热心的网友已经帮我们重新实现了这个逻辑。代码如下:

 
function get_type($var)
{
    if (is_object($var)) {
        return get_class($var);
    }
    if (is_null($var)) {
        return 'null';
    }
    if (is_string($var)) {
        return 'string';
    }
    if (is_array($var)) {
        return 'array';
    }
    if (is_int($var)) {
        return 'integer';
    }
    if (is_bool($var)) {
        return 'boolean';
    }
    if (is_float($var)) {
        return 'float';
    }
    if (is_resource($var)) {
        return 'resource';
    }
    return 'unknown';
}

另一个版本

/**
 * Returns the type of the var passed.
 *
 * @param mixed $var Variable
 *
 * @return string Type of variable
 */
function myGetType($var)
{
    if (is_array($var)) {
        return "array";
    }
    if (is_bool($var)) {
        return "boolean";
    }
    if (is_float($var)) {
        return "float";
    }
    if (is_int($var)) {
        return "integer";
    }
    if (is_null($var)) {
        return "NULL";
    }
    if (is_numeric($var)) {
        return "numeric";
    }
    if (is_object($var)) {
        return "object";
    }
    if (is_resource($var)) {
        return "resource";
    }
    if (is_string($var)) {
        return "string";
    }
    return "unknown type";
}

 

阅读全文

原文地址:理解Javascript的闭包

前言:还是一篇入门文章。Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点ECMAScript语言规范来使读者可以更深入的理解闭包。

注:本文是入门文章,例子素材整理于网络,如果你是高手,欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript,不想做语言对比,如果您对Javascript天生不适,请自行绕道。

什么是闭包

闭包是什么?闭包是Closure,这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是:

• 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。

• 闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配

• 当在一个函数内定义另外一个函数就会产生闭包

上面的第二定义是第一个补充说明,抽取第一个定义的主谓宾——闭包是函数的‘局部变量’集合。只是这个局部变量是可以在函数返回后被访问。(这个不是官方定义,但是这个定义应该更有利于你理解闭包)

做为局部变量都可以被函数内的代码访问,这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”,或将这个”引用”赋值给某个外部变量,才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个对象,因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是,ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变量。但是在ECMAScript中,函数对象中定义的内部函数() inner function是可以直接访问外部函数的局部变量,通过这种机制,我们就可以以如下的方式完成对闭包的访问了。

function greeting(name) {
    var text = 'Hello ' + name; // local variable
    return function() { alert(text); }  // 每次调用时,产生闭包,并返回内部函数对象给调用者
}
var sayHello=greeting("Closure");
sayHello()  // 通过闭包访问到了局部变量text

上述代码的执行结果是:Hello Closure,因为sayHello()函数在greeting函数执行完毕后,仍然可以访问到了定义在其之内的局部变量text。

好了,这个就是传说中闭包的效果,闭包在Javascript中有多种应用场景和模式,比如Singleton,Power Constructor等这些Javascript模式都离不开对闭包的使用。

ECMAScript闭包模型

ECMAScript到底是如何实现闭包的呢?想深入了解的亲们可以获取ECMAScript 规范进行研究,我这里也只做一个简单的讲解,内容也是来自于网络。

在ECMAscript的脚本的函数运行时,每个函数关联都有一个执行上下文场景(Execution Context) ,这个执行上下文场景中包含三个部分

  • 文法环境(The LexicalEnvironment)
  • 变量环境(The VariableEnvironment)
  • this绑定

其中第三点this绑定与闭包无关,不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象,该对象包含了两个重要组件,环境记录(Enviroment Recode),和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量,外部引用指向了外部函数对象的上下文执行场景。全局的上下文场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表,每个引用都指向外层的上下文场景。

例如上面我们例子的闭包模型应该是这样,sayHello函数在最下层,上层是函数greeting,最外层是全局场景。如下图:

因此当sayHello被调用的时候,sayHello会通过上下文场景找到局部变量text的值,因此在屏幕的对话框中显示出”Hello Closure”

变量环境(The VariableEnvironment)和文法环境的作用基本相似,具体的区别请参看ECMAScript的规范文档。

闭包的样列

前面的我大致了解了Javascript闭包是什么,闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包,下面共有5个样例,例子来自于JavaScript Closures For Dummies(镜像)

例子1:闭包中局部变量是引用而非拷贝

function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() { alert(num); }
    num++;
    return sayAlert;
}
var sayAlert = say667();
sayAlert()

因此执行结果应该弹出的667而非666。

例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。

function setupSomeGlobals() {
    // Local variable that ends up within closure
    var num = 666;
    // Store some references to functions as global variables
    gAlertNumber = function() { alert(num); }
    gIncreaseNumber = function() { num++; }
    gSetNumber = function(x) { num = x; }
}
setupSomeGolbals(); // 为三个全局变量赋值
gAlertNumber(); //666
gIncreaseNumber();
gAlertNumber(); // 667
gSetNumber(12);//
gAlertNumber();//12

例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + list[i];
        result.push( function() {alert(item + ' ' + list[i])} );
    }
    return result;
}
function testList() {
    var fnlist = buildList([1,2,3]);
    // using j only to help prevent confusion - could use i
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

testList的执行结果是弹出item3 undefined窗口三次,因为这三个函数绑定了同一个闭包,而且item的值为最后计算的结果,但是当i跳出循环时i值为4,所以list[4]的结果为undefined.

例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。

function sayAlice() {
    var sayAlert = function() { alert(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return sayAlert;
}
var helloAlice=sayAlice();
helloAlice();

执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后,局部变量仍然可以被访问到。

例子5:每次函数调用的时候创建一个新的闭包

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        alert('num: ' + num +
        '\\nanArray ' + anArray.toString() +
        '\\nref.someVar ' + ref.someVar);
    }
}
closure1=newClosure(40,{someVar:'closure 1'});
closure2=newClosure(1000,{someVar:'closure 2'});
closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
closure2(-10);// num:990 anArray[1,2,3,990] ref:'someVar closure2'

闭包的应用

Singleton 单件:

var singleton = function () {
    var privateVariable;
    function privateFunction(x) {
        ...privateVariable...
    }
    return {
        firstMethod: function (a, b) {
            ...privateVariable...
        },
        secondMethod: function (c) {
            ...privateFunction()...
        }
    };
}();

这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法,方法1可以方法私有变量,方法2访问内部私有函数。需要注意的地方是匿名主函数结束的地方的’()’,如果没有这个’()’就不能产生单件。因为匿名函数只能返回了唯一的对象,而且不能被其他地方调用。这个就是利用闭包产生单件的方法。

参考:

JavaScript Closures For Dummies(镜像) 可惜都被墙了。

Advance Javascript (Douglas Crockford 大神的视频,一定要看啊)

原文地址:理解Javascript的闭包

另外一篇 Javascript中Closure及其相关概念  感觉也不错。

关于js闭包的问题 自己还是需要好好地研究研究

阅读全文

原文地址:JS实现拖动div层移动

JS实现拖动div层移动

      在谈到拖动div层之前,我们有必要来了解下 下面JS几个属性的区别—-  pageX,pageY,layerX,layerY,clientX,clientY,screenX,screenY,offsetX之间的区别!

     PageX: 鼠标在页面上的位置,从页面左上角开始,即是以页面为参考点,不随滑动条移动而变化.(只有firefox等标准游览器特有,IE没有)。
clientX: 鼠标在页面上可视区域的位置,从浏览器可视区域左上角开始,即是以浏览器滑动条此刻的滑动到的位置为参考点,随滑动条移动 而变化.

这两个最主要的区别是 在有滚动条的情况下,pageX是不随滚动条变化而变化,clientx是在可视区域内的距离,不包括滚动条的距离。

  screenX: 鼠标在屏幕上的位置,从屏幕左上角开始,这个没有任何争议.

     offsetX和layerX

      offsetX   IE特有,鼠标相比较于触发事件的元素的位置,以元素盒子模型的内容区域的左上角为参考点,如果有boder,可能出现负值。

      layerX:   firefox特有,鼠标相比较于当前坐标系的位置,即如果触发元素没有设置绝对定位或相对定位,以页面为参考点,如果有,将改变参考坐标系,从触发元素盒子模型的border区域的左上角为参考点 也就是当触发元素设置了相对或者绝对定位后,layerX和offsetX就幸福地生活在一起^-^,几乎相等,唯一不同就是一个从border为参考点,一个以内容为参考点,FF从border开始.

    pageX,pageY只有firefox特有,IE没有,所以要针对游览器兼容性写个函数,Jquery源码中 这样写的,

    所以我们也可以针对写个公用的函数,代码如下:

function pageXY(e) {
    var event = e || window.event;

    var doc = document.documentElement,
          body = document.body;

    // IE
    if (event.pageX == null && event.clientX !=  null ) {
        var doc = document.documentElement,
            body = document.body;
            event.pageX = event.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );

            event.pageY = event.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );

            return {
                x : event.pageX,
                y : event.pageY
            }
        }

        // firefox
        return {
            x : event.pageX,
            y : event.pageY
        }
    }

offsetX 是IE特有的 layerX是firefox特有的,所以针对这两个也可以写个公用的函数 代码如下:

 

function offsetXY(e) {
    var event = e || window.event;
    return {
         x:event.offsetX || event.layerX,
                 y:event.offsetY || event.layerY
    }
}

 

JSFiddle链接代码如下:

 想看div层拖动的话 请点击我!

拖动层的基本原理是:

首先先来理解下 我们要在页面上拖动某一块 到 页面上的另外一个位置 那么肯定这块元素是绝对定位的 并且 我们移动它时 是不断的改变他们的top值和left值!再者 我们拖动它时候肯定要触发事件!有 onmousedown事件!

  那么我们现在是要计算的是 我们这个元素被拖动到页面上的某个位置时的 左上标的位置X和Y。

      如下图所示:

      

 

要计算元素的左上的x和y坐标 如上图所示:就是指x = clientX-offsetX + “px”; y= clientY-offsetY + “px”;

HTML和CSS代码如下:

 

<div id="father" style="border:0px solid red;width:200px;">
    <div id="a" style="background:red;width:100px;height:100px">长,宽都是100px</div>
    <div id="b" style="border-top:0px solid red;background:yellow;width:100px;height:100px;margin-left:100px;"></div>
    </div>
<style>
    #oDiv{ width:200px; height:200px; color:#fff;background:#00C; position:absolute; top:200px; left:200px; z-index:100;overflow:hidden;}
 </style>

 

JS所有代码如下:

function pageXY(e) {
    var event = e || window.event;

    var doc = document.documentElement,
        body = document.body;

    // IE
    if (event.pageX == null && event.clientX !=  null ) {
        var doc = document.documentElement,
            body = document.body;

            event.pageX = event.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );

            event.pageY = event.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );

            return {
                x : event.pageX,
                y : event.pageY
            }
        }

        // firefox
        return {
            x : event.pageX,
            y : event.pageY
        }
    }

    function offsetXY(e) {
        var event = e || window.event;
        return {
             x:event.offsetX || event.layerX,
                         y:event.offsetY || event.layerY
        }
    }
    var $=function(id){
        return ("string"==typeof id) ? document.getElementById(id):id;
    };
    $('a').onmousemove = function(e){
        text(e);
    }
    $('b').onmousemove = function(e){
        text(e);
    }
    function text(e) {
        e = e || window.event;
        var offset = offsetXY(e),
            page = pageXY(e);

        var doc = document.documentElement,
            body = document.body;
                $("pageX").innerHTML= page.x;
        $("pageY").innerHTML= page.y;
        $("clientX").innerHTML=e.clientX;
        $("clientY").innerHTML=e.clientY;
        $("screenX").innerHTML=e.screenY;
        $("screenY").innerHTML=e.screenY;
        $("scrollTop").innerHTML=doc && doc.scrollTop;
        $("scrollLeft").innerHTML=doc && doc.scrollLeft;
        $("offsetX").innerHTML = offset.x;
        $("offsetY").innerHTML = offset.y;
    }

    window.onload = function () {
            var oDiv = document.getElementById("oDiv");//oDiv必须使用CSS定位
            oDiv.onmousedown = drag;
            function drag(evt) {
                evt = evt || window.event;
                this.onmouseup = drop;
                this.onmousemove = moveDiv;
                this.offset = {
                    x:evt.offsetX || evt.layerX, //layerX 和layerY是w3c标准的 offsetX 和 offsetY是IE标准的
                    y:evt.offsetY || evt.layerY
                };
            }
            function moveDiv(evt) {
                evt = evt || window.event;
                this.style.left = evt.clientX-this.offset.x+"px";
                this.style.top = evt.clientY-this.offset.y+"px";
            }
            function drop(evt) {
                this.onmouseup = null;
                this.onmousemove = null;
            }
        };

原文地址:JS实现拖动div层移动

另一个使用JQUERY的文章为:Jquery 实现层的拖动,支持回调函数

还有一个 “自己写了一个无缝滚动的插件(jQuery)”也很有意思

阅读全文

JSON的标准格式如下:
1.所有的键必须用双引号包裹
2.JSON不支持注释
3.JSON的值不得为函数不得未定义。值可以是用双引号包裹的字符串,或是数字,或true、false,或null,或一个对象、数组。允许嵌套结构。
4.JSON的最后一个值不使用逗号(,)分隔符

更相信的请点这里

 

阅读全文

原文地址:由蔡文胜骗股有感——技术人员,你除了技术,还有啥

近闻悉,中国著名天使投资人——蔡文胜,对其4399cto曹政股权被稀释一事,蔡文胜并未否认,他表示自己早已卸任4399董事长职务,股权的事 是4399公司内部的事情。其中来龙去脉,作为局外人也不好评论,但程序员们,你们除了技术外,是不是要有另外立足与世界的本领。

不错,程序员,本应是一个神圣职业,你们正是利用你们手中武器改变这世界。但是,世界终归是人的世界,终归是情感的世界,不是机器的世界。所以 了一般做技术的人,显得与这个世界显得格格不入。一般喜欢技术的人,就木讷,和机器相处多了,就显的更加的木讷。被具有中国特色的人情社会所抛弃,也很有 可能,呗坑蒙拐骗也是理所当然的。因此,技术人员,你除了技术,应该还有啥把。那请提高你的人文修养把!

作为一个技术人员,想成为管理者并不难,只要你坚持这个工作。在国内的氛围中,当技术人员,在经验、能力、年龄达到一定层面后,公司都会想办法提升 他的职位,进入管理岗位,如项目经理、研发主管、技术部经理、技术总监等等。很少会有公司能允许一个三十多岁的程序员存在,即便他做着技术工作,也会给他 安个什么大牌的头衔。

当进入管理岗位后,痛苦就开始了。专业技术人员与技术管理人员,虽然都是技术性工作,前者侧重于技术,后则侧重于管理,工作性质已经有了明显区别。 相较于技术,管理工作更复杂。管理工作是面对人,技术工作是面对电脑,人比电脑复杂得太多了。没有学不会的技术,管理则需要岁月历炼。管理的英语单词 “Manage”,就是男人与岁月的合成词。

国王的命运

管理性工作,不管你做的有多好,总会有人给你提意见,总会有人不满意。相比较而言,做技术性工作,真是太幸福了,不用操心,专心至致,不会有人打扰,做起来也顺手,工作成果总会受到大家的赞誉。

当我在做经理后,总是忍不住回过头来写代码,一方面是因为管理能力不足,任务分解不出去,一方面是对他们做的工作不满意,最主要的还是,写代码的感 觉实在太幸福了。虽然我现在也写代码,但目的已经不是为了体验幸福,而是觉得作为一个技术方面的管理者,不应该丢弃技术,工作重点已经不是程序编写。

在那个阶段,我非常迷茫,不知道怎么才能把工作做好,团队成员整天提意见,公司领导责备多于鼓励,我的自信心跌到了冰点,甚至有想过放弃管理工作。但说实话,又有些不甘心,也有些中国人官本位思想,毕竟是混到个小领导了,真是再下来了,多没有面子。

在这个过程中,我看了很多管理的书、管理的讲座,始终不得要领。有一次在书店闲逛,看到《沉思录》随手拿起来翻看,我知道这本书,之前中央领导向社会推荐读物,温家宝推荐了《沉思录》。也就是这一翻,看到了将会影响我终生的一句话(一点儿也不夸张)。

“国王的命运,就是行善事而遭恶誉”。

我毫不犹豫的买下这本书,如果一本书有一句话值得拥有,这几十块钱就花值了。

前一段,有个朋友刚当了项目经理,很痛苦,在QQ上向我诉苦。我说,我送你一句话,“国王的命运,就是行善事而遭恶誉”,管理者就要有王的胸怀,不 仅要敢于担当,还要有胸襟承受非议,相信自己是对的,做就是了。几秒钟后,他的QQ签名变成了这句话;几个月后,说他感觉自己已经可以胜任了。

在我的管理工作中,每次提拔主管时,我都会赠送这句话给他们。我不担心他们的技术能力,我担心他们的心理承受能力。其实这句话,有些类似于“走自己路,让别人去说吧”的意思,但是它更有气魄。

《梓人传》

当刚开始做管理工作时,任务分解是一个让我非常头痛的问题。

如何能够合理的把任务分配给团队成员,让他们轻松完成,而且又能保证质量,这对我来说,实在是个难以解决的问题。

往往是,任务分解不出去,自己埋头干活,团队进度没时间把控;要么就是团队成员做完工作,不合要求,自己伸手把任务又重做一遍。很显然,我做得更好,更快。看着程序员们崇拜的目光,也有点小小的成就感。可到头来,自己累得要死,并没有落好,还净落埋怨。

在这个过程中,我着重学习了项目管理,后来还考了一个信息系统的项目管理师。我很清楚,自己应该高屋建棱,我还自己总结了一句话,“低姿态工作,高视角审视”。事实上,我根本不知道该如何去“高视角”。虽然书上讲了很多方法,但操作起来总是不能令自己满意。

很偶然的,我在图书馆看杂书,看到柳宗元的《梓人传》,有些感触,后来上班时无事时,在网又搜这篇文章,仔细的阅读,还看了很多人的点评。虽然没有醍醐灌顶吧,但也受用良多。

例如文中所述:

“彼将舍其艺,而专其心智,能知体要欤”

“不炫能,不矜名,不亲小劳,不侵众宫,日与天下英才讨论其大经。如梓人之善运众工,而不伐艺。”

它说的这些原则,我全违背了,思路不正确,方法再正确也没有用。

法、术、势

为了规范开发行为,我制订了很多制度,因为从我自身管理思想上来说,我是崇尚制度化管理的。我学习了很多开发方法、开发模式,研习了很久,但任何一 种方法,应用到研发工作中,都是一堆问题。当有人质疑时,我总会引章据典的予以辩论,说得对方无可反驳,其实我也清楚,他是“口服心不服”。

我不知道自己哪里出了问题,我相信自己学习的东西都是正确的。为什么就实施不起来呢?

前几天和一个朋友还在聊这类事情,他也很迷茫,我告诉他,其实我也经历过这个阶段,我建议你有时间看看《韩非子》。我当时就是认真的看了《韩非子》(其实也没有看完了,至少比以前看得仔细了)之后,才有了较大改观。

《韩非子》是法家思想经典之作。我们总是以为,法家思想提倡“法”,就是制度化管理。事实上,法家的精髓不仅是“法”,而是包括:法、术、势。

法,是制度,是条例,也是规范,它有一定的约束性,但它不是为了约束,而是为了规范。

术,是手段,是技巧,例如,当发现别人做得不符合要求时,不要批评,而应该说:“你做得很好,但我认为,还有很大的提升空间。”

势,是权力,是职责,例如,韩非子中说,桀纣虽然很坏,但如果不是坐在王的位置上,也不能危害天下;尧舜虽然贤明,如果不是坐在王的位置上,也不能造福天下。

我们评论法家思想,总会说“法太苛”,并举秦朝失败的案例,事实上,至从秦朝之后,无论哪个朝代都在延用法家思想,也就是所谓的“儒表法里”(表面 是儒,其实是法)。到了唐朝,则大范围照搬秦制,为了不让别人说闲话,柳宗元说了句名言为唐朝开脱,“秦之失,失于政,非失于制”,就是说秦朝的制度是非 常好的,是他们管理上出了问题。

应用到我们管理中,制度化是必须的,其次还要考虑管理手段问题,不能完全依靠制度。最后,就是把合适的人放到合适的位置,明确其职责。

关于“法”,我们可以学习别人,甚至照搬。而“术”,则需要因地制异,需要管理艺术。而“势”,则需要因人而异、量才适用,它不仅需要高超的管理艺术,还要大胆授权、敢于放权。

注意沟通

学会倾听
·倾听的技巧
倾听是一种情感活动,在倾听时应该给客户充分的尊重、情感的关注和积极的回应。

1.倾听的定义

倾听是一种情感的活动,它不仅仅是耳朵能听到相应的声音。倾听还需要通过面部表情,肢体的语言,还有用语言来回应对方,传递给对方一种你很想听他说话的感觉,因此我们说倾听是一种情感活动,在倾听时应该给客户充分的尊重、情感的关注和积极的回应。

【案例】

倾听的“听”字在繁体中文是听字里有一个“耳”字,说明听字是表示用耳朵去听的;听字的下面还有一个“心”字,说明倾听时要用“心”去听;听字里还有一个“目”字,说明你听时应看着别人的眼睛地听;在“耳”的旁边还有一个“王”字,“王”字代表把说话的那个人当成是帝王来对待。

从听字的繁体结构中可以看出,倾听时不仅要用“耳朵”,还要用“心”,用“眼睛”,更重要的是要把你对面的那个人当成是帝王,充分地去尊重他。

2.听事实和情感

倾听不但要听清楚别人在讲什么,而且要给予别人好的感觉,那么听时服务代表都在听什么呢?对服务代表来说,需要听两点:

◆听事实

倾听事实意味着需要能听清楚对方说什么。要做到这一点,就要求服务代表必须有良好的听力。

◆听情感

与听事实相比,更重要的是听情感。服务代表在听清对方说事实时,还应该考虑客户的感受是什么,需不需要给予回应。

【案例】

A对B说:“我昨天看中一套房子,决定把它买下来。”B说:“哦,是吗?在哪儿呢?恭喜你呀。”A看中了房子,想买下来,这是一个事实,B问房子在那,这是对事实的关注,“恭喜你”就是对A的情感关注。

A把事实告诉B,是因为他渴望B与他共同分享他的喜悦和欢乐,而作为B,应对这种情感去加以肯定。对于服务代表而言,就是运用倾听的技巧,通过你的面部表情,肢体语言,给予客户恰当的及时回应。例如客服人员对客户说:“现在你就是这方面的专家,你真的是很内行。”这就是对客户的一种情感的关注。而在这种关注之前,服务代表在听到客户谈话时应该分辨出哪些是情感的部分,哪些是事实的部分。

3.提升倾听能力的技巧

◆永远都不要打断客户的谈话

可以这样说,在这个世界上就应该没有一个人说我喜欢或习惯打断过别人的谈话,很多时候一些人的倾听能力是很差的,他们都不是无意打断,而是有意识地打断对方的谈话。

无意识的打断是可以接受的,有意识的打断却是绝对不允许的。无意识地打断客户的谈话是可以理解的,但也应该尽量避免;有意识地打断别人的谈话,对于客户来讲是非常不礼貌的。当你有意识地打断一个人说话以后,你会发现,你就好像挑起来了一场战争,你的对手会以同样的方式来回应你,最后你们两个人谈话就可能变成了吵架。因此有意识的打断是绝对不允许的。

◆清楚地听出对方的谈话重点

当你与对方谈话时,如果对方正确地理解了你谈话中的意思,你一定会很高兴。至少他知道你成功地完成了我们上边所说的“听事实”的层面。

能清楚地听出对方的谈话重点,也是一种能力。因为并不是所有人都能清楚地表达自己的想法,特别是在不满,受情绪的影响的时候,经常会有类似于“语无伦次”的情况出现。而且,除了排除外界的干扰,专心致志地倾听以外,你还要排除对方的说话方式给你的干扰,不要只把注意力放在说话人的咬舌、口吃、地方口音、语法错误或“嗯”、“啊”等习惯用语上面。

◆适时地表达自己的意见

谈话必须有来有往,所以要在不打断对方谈话的原则下,也应适时地表达自己的意见,这是正确的谈话方式。这样做还可以让对方感受到,你始终都在注意地听,而且听明白了。还有一个效果就是可以避免你走神或疲惫。

◆肯定对方的谈话价值

在谈话时,即使是一个小小的价值,如果能得到肯定,讲话者的内心也会很高兴的,同时对肯定他的人必然产生好感。因此,在谈话中,一定要用心地去找对方的价值,并加以积极的肯定和赞美,这是获得对方好感的一大绝招。比如对方说:“我们现在确实比较忙”,你可以回答:“您坐在这样的领导位子上,肯定很辛苦。”

◆配合表情和恰当的肢体语言

当你与人交谈时,对对方活动的关心与否直接反映在你的脸上,所以,你无异于是他的一面镜子。

光用嘴说话还难以造成气势,所以必须配合恰当的表情,用嘴、手、眼、心灵等各个器官去说话。但要牢记切不可过度地卖弄,如过于丰富的面部表情、手舞足蹈、拍大腿、拍桌子等。

◆避免虚假的反应

在对方没有表达完自己的意见和观点之前,不要做出比如“好!我知道了”、“我明白了”、“我清楚了”等反应。这样空洞的答复只会阻止你去认真倾听客户的讲话或阻止了客户的进一步的解释。

在客户看来,这种反应等于在说“行了,别再罗嗦了”。如果你恰好在他要表达关键意思前打断了他,被惹恼了的客户可能会大声反抗:“你知道什么?”那就很不愉快了。

·倾听——团队沟通的艺术

在团队沟通中,言谈是最直接、最重要和最常见的一种途径,有效的言谈沟通很大程度上取决于倾听。

有一个古老的哲学问题:“森林中一棵树倒了下来,那儿不会有人听到,那么能说它发出声响了吗?”关于沟通,我们也可以问类似的问题:如果你说话时没人听,那么能说你进行沟通了吗?

团队沟通中,言谈是最直接、最重要和最常见的一种途径,有效的言谈沟通很大程度上取决于倾听。作为团体,成员的倾听能力是保持团队有效沟通和旺盛生命力的必要条件;作为个体,要想在团队中获得成功,倾听是基本要求。在对美国500家最大公司进行的一项调查表明,做出反应的公司中超过50%的公司为他们的员工提供听力培训。有研究表明:那些是很好的倾听者的学生比那些不是的学生更为成功。在工作中,倾听已被看作是获得初始职位、管理能力、工作成功、事业有成、工作出色的重要必备技能之一。
在倾听的过程中,如果人们不能集中自己的注意力,真实地接受信息,主动地进行理解,就会产生倾听障碍。在人际沟通中,造成信息失真。影响倾听效率障碍不外乎以下三点:

1.环境干扰

环境对人的听觉与心理活动有重要影响,环境中的声音、气味、光线以及色彩、布局,都会影响人的注意力与感知。布局杂乱、声音嘈杂的环境将会导致信息接收的缺损。

2.信息质量低下

双方在试图说服、影响对方时,并不一定总能发出有效信息,有时会有一些过激的言辞、过度的抱怨,甚至出现对抗性的态度。现实中我们经常遇到满怀抱怨的顾客,心怀不满的员工,剑拔弩张的争论者。在这种场合,信息发出者受自身情绪的影响,很难发出有效的信息,从而影响了倾听的效率。

信息低下的另一个原因是,信息发出者不善于表达或缺乏表达的愿望。例如,当人们面对比自己优越或地位高的人时,害怕“言多必失”以致留下坏印象,因此不愿意发表自己的意见,或尽量少说。

3.倾听者主观障碍

在沟通的过程中,造成沟通效率低下的最大原因就在于倾听者本身。研究表明,信息的失真主要是在理解和传播阶段,归根到底是在于倾听者的主观障碍。

(1)个人偏见 即使是思想最无偏见的人也不免心存偏见。在一次国际会议上,以色列代表团的成员们在阐述其观点时,用了非常激烈的方式,他们抱怨泰国代表对会议不表示任何兴趣或热情,因为他们“只是坐在那里”,而泰国代表则认为以色列教授非常愤怒,因为他们“用了那么大的嗓门”。所以,在团队中成员的背景多样化时,倾听者的最大障碍就在于自己对信息传播者偏见,而无法获得准确的信息。

(2)先入为主 在行为学中被称为“首因效应”,它是指在进行社会知觉的过程中,对象最先给人留下的印象,对以后的社会知觉发生重大影响。也就是我们常说的,第一印象往往决定了将来。人们在倾听过程中,对对方最先提出的观点印象最深刻,如果对方最先提出的观点与倾听者的观点大相径庭,倾听者可能会产生抵触的情绪,而不愿意继续认真倾听下去。

(3)自我中心 人们习惯于关注自我,总认为自己才是对的。在倾听过程中,过于注意自己的观点,喜欢听与自己观点一致的意见,对不同的意见往往是置若罔闻,这样往往错过了聆听他人观点的机会。

掌握倾听的艺术并非很难,只要克服心中的障碍,从小节作起,肯定能够成功。现列出一些提高倾听能力的技巧以便核对、参考:

1.创造有利的倾听环境,尽量选择安静、平和的环境,使传递者处于身心放松的状态。

2.在同一时间内既讲话又倾听,这是不可能的事情,要立即停止讲话,注意对方的讲述。

3.尽量把讲话时间缩到最短。你讲话时,便不能聆听别人的良言,可惜许多人都忽略了这一点。

4.摆出有兴趣的样子。这是让对方相信你在注意聆听的最好方式,是发问和要求阐明他正在讨论的一些论点。

5.观察对方。端详对方的脸、嘴和眼睛,尤其要注视眼睛,将注意力集中在传递者的外表。这能帮助你聆听,同时,能完全让传递者相信你在聆听。

6.关注中心问题,不要使你的思维迷乱

7.平和的心态,不要将其他的人或事牵扯进来。

8.注意自己的偏见,倾听中只针对信息而不是传递信息的人。诚实面对、承认自己的偏见,并能够容忍对方的偏见。

9.抑制争论的念头。注意你们只是在交流信息,而非辩论赛,争论对沟通没有好处,只会引起不必要的冲突。学习控制自己,抑制自己争论的冲动,放松心情。

10.保持耐性,让对方讲述完整,不要打断他的谈话,纵然只是内心有些念头,也会造成沟通的阴影。

11.不要臆测。臆测几乎总是会引导你远离你的真正目标,所以要尽可能避免对对方做臆测。

12.不宜过早做出结论或判断。人往往立即下结论,当你心中对某事已做了判断时,就不会再倾听他人的意见,沟通就被迫停止。保留对他人的判断,直到事情清楚,证据确凿。

13.做笔记。做笔记不但有助于聆听,而且有集中话题和取悦对方的优点。如果有人重视你所说的话并做笔记,你不会受宠若惊吗?

14.不要自我中心,在沟通中,只要把注意力集中在对方身上,才能够进行倾听。但很多人习惯把注意力集中在自己身上,不太注意别人,这容易造成倾听过程的混乱和矛盾。

15.鼓励交流双方互为倾听者。用眼神、点头或摇头等身体语言鼓励信息传递者传递信息和要求别人倾听你的发言。

·积极倾听

这个世界的图像并不是自动进入我们大脑的,而是有选择的。我们不是在看,而是在寻觅着什么。我们不是听见世界上所有的声音,只是在听。

积极倾听是一种非常好的回应方式,既能鼓励对方继续说下去,又能保证你理解对方所说的内容。要熟练地使用这种技巧,首先要知道,当别人和你说话时,发生着什么样的事情。

人际交往首先源于个人内心。对方先是有一些感受或者想法想告诉你。为了传递这个信息,他首先必须将其转换成语言以及非语言代码,以便你能够理解。至于他选择什么样的代

码,什么样的语言和动作,以及说话时的音调,会由他的目的、所处环境、和你的关系亲密程度,以及他的年龄、教育背景、社会地位、文化背景和感情状况所决定。这个把内心的想法和感受转换成信息的过程被称为编码。

例如,假设你在给一个朋友播放音乐。他很喜欢,却希望能柔和一些。你无法知道他头脑中的想法,于是为了让你知道,他把自己的感受编码,用盖过音乐的声音对你说:“声音关小点儿!”
这样,你有了技术,还有更多。

原文地址:由蔡文胜骗股有感——技术人员,你除了技术,还有啥

PS:感觉这篇文章 博主写的很好。在“景德镇”咱不能一辈子coding,如果咱想在IT行业一直有优势、可以站得住脚的话,咱就要往上爬,需要学点管理;管理和咱们coding不那么一样,需要学的东西还有很多很多,各种为人处世的本事。。。

阅读全文