PHP初体验
PHP初体验
提笔写初体验总不知道从何说起,直接聊PHP中的函数、PHP网络技术、数据库操作、PHP模板等感觉又不是初体验。最后还是决定从PHP的面向对象、PHP的魔术方法、PHP的反射、PHP中的异常和错误这4个方面简单介绍一下。
PHP面向对象的“形”与“本”
这里我们就不给面向对象下定义了,不过我们还是要说一下类和对象的。类是对象的抽象组织,对象是类的具体存在。接下来我们就拿PHP为例,来探讨一下对象的“形”与“本”的问题。
在PHP中,每个类的定义都是以关键字class开头,后面是类名和一对花括号,括号中包含类成员和方法的定义。如下是一个简单类的定义:
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
} public function sayHello(){
echo "My name is $this->name and I am $this->age years old. I am a $this->sex.\r\n";
echo self::$information;
echo "<br>";
}
}
$person = new Person();
$person->name = 'Lisi';
$person->sayHello();
echo serialize($person); //输出结果如下:
//My name is Lisi and I am 23 years old. I am a male. I come from the earth
//O:6:"Person":3:{s:4:"name";s:4:"Lisi";s:11:"Personage";i:23;s:11:"Personsex";s:4:"male";}
Person类
当把类对象序列化输出时,可以看出类对象在存储时类似于数组的形式。那么类对象与数组从本质上又有什么区别与联系呢?接下来从对象“本”来分析一下PHP对对象的底层实现。如下是PHP源码中对变量的定义:
#变量定义
typedef struct _zval_struct zeal; //存储变量的类型 struct _zval_struct {
/* Variable information */
zvalue_value value; /* 存储变量的值 */
zend_uint refcount; /* 变量引用数 */
zend_uchar type; /* 变量类型 */
zend_uchar is_ref; /* 变量是否被引用 */
}; typedef union _zvalue_value {
long lval; /* 长整型 */
double dval; /* double型 */
struct {
char *val;
int len;
} str; /* String型 */
HashTable *ht; /* array型 */
zend_object_value obj; /* 对象型 */
} zvalue_value; #对象底层实现
typedef struct _zend_object {
zend_class_entry *ce; //类入口,存储该对象的类结构,相当于类指针
HashTable *properties; //对象属性组成的HashTable
HashTable *guards; /* 阻止递归调用 */
} zend_object; typedef struct _zend_guard {
zend_bool in_get;
zend_bool in_set;
zend_bool in_unset;
zend_bool in_isset;
zend_bool dummy; /* sizeof(zend_guard) must not be equal to sizeof(void*) */
} zend_guard;
//由于zend_class_entry源码较多且复杂,因此此处省略。
PHP源码中变量、对象的定义
通过上面的代码我们也对PHP如何存储对象有了初步的认识,那对象与数组又是什么关系呢?通过PHP的源码可得,zvalue_object结构中有一个HashTable的类型,它就是存储数组的。PHP对象的结构体中不仅有HashTable(用于存储类对象特有的属性),而且还有对象所属类的入口等,如下是PHP对象的组成:
其中PHP源码中zend_class_entry结构体中存储的就是类的指针,该结构体中包含类常量、静态属性、标准方法、魔术方法、自定义方法等。而属性数组存储的是类对象的属性。接下来我们还是以如上的Person类为例,谈一谈对象与数组:
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
}
$person = new Person();
$person_arr = array("name"=>"zhangsan", "age"=>"23", "sex"=>"male");
echo serialize($person); echo "<br>"; //输出对象序列化结果并换行符
echo serialize($person_arr); echo "<br>"; //输出数组序列化结果并换行
$object = (object)$person_arr; //将数组转化为对象
echo serialize($object); //输出数组序转化为对象的序列化结果 /*输出结果如下:
O:6:"Person":3:{s:4:"name";s:8:"zhangsan";s:11:"Personage";i:23;s:11:"Personsex";s:4:"male";}
a:3:{s:4:"name";s:8:"zhangsan";s:3:"age";s:2:"23";s:3:"sex";s:4:"male";}
O:8:"stdClass":3:{s:4:"name";s:8:"zhangsan";s:3:"age";s:2:"23";s:3:"sex";s:4:"male";} 结果解释如下:
O代表的是对象,a代表的是数组,
O后的数字(6和8)代表该对象所属类名的长度,紧挨在数字后面的就是类名
类名后面的数字为类对象属性的个数,如上有3个,分别为name,age,sex
大括号中的为对象或数组的属性名和属性值的键值对,其中s代表字符串
最后需要说明的是当把数组转化为对象时,因为没有与数组转换成对象对应的类,因此PHP中一个称为"孤儿"的类stdClass类就会收留这个对象
*/
PHP中的魔术方法
可能细心的你在对象组成的那张图中看到了魔术方法,但是上一节中并没有对zend_class_entry中的内容做任何介绍。那么什么又是魔术方法呢?魔术方法就是以两个下划线“__”开头、具有一些特殊作用的方法。其实如上的Person类中,我们不经意间就使用了魔术方法__construct(),这个魔术方法就是构造方法。用于在创建类对象时对属性进行赋值。接下来我们将介绍几个常见的魔术方法让大家对魔术方法有个初步了解。
- __sleep():该魔术方法是在执行serialize()方法完成前被调用,该方法可以用来关闭对象可能具有的任何数据库连接、加密需要序列化对象属性的值、指定仅序列化对象的某些属性等等。
- __wakeup():该魔术方法是在执行unserialize()方法完成前被调用,该方法可以用来建立对象可能的数据库连接、解密序列化对象属性的值等等。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __sleep() {
// TODO: Implement __sleep() method
return array("name","age"); //该方法指定仅序列化对象的name和age属性
}
public function __wakeup() {
// TODO: Implement __wakeup() method.
var_dump($this->name); //打印输出对象的name属性的值
var_dump($this->sex); //打印输出对象的sex属性的值,由于sex没有被序列化,因此输出null
}
}
$serResult = serialize(new Person()); //序列化一个Person类对象,该方法完成前先调用Person类的__sleep()
echo $serResult; echo "<br>"; //输出对象序列化结果并换行符
$unSerResult = unserialize($serResult); //将序列化结果反序列化,该方法完成前调用Person类的__wakeup方法
echo $unSerResult->name; echo "<br>"; //由反序列化得到的Person类对象调用对象的name属性
echo var_dump($unSerResult); echo "<br>"; //输出反序列化得到的Person类对象 /*输出结果如下:
O:6:"Person":2:{s:4:"name";s:8:"zhangsan";s:11:"Personage";i:23;}
string(8) "zhangsan" NULL zhangsan
object(Person)#1 (3) { ["name"]=> string(8) "zhangsan" ["age":"Person":private]=> int(23) ["sex":"Person":private]=> NULL }
*/__sleep()和__wakeup()方法
- __construct():类的构造方法,类对象在创建时会首先调用该方法,因此该方法中可以做一些类对象创建前的初始化工作。
- __destruct():类的析构方法,当类对象在销毁时会调用该方法。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __destruct() {
// TODO: Implement __destruct() method.
echo "The object will be destructed";
}
} $person = new Person();
unset($person); //unset()方法释放$person对象
echo $person->name; //该行试图打印$person对象的name属性值,由于$person对象已经被销毁了,因此该行会报错
/*
输出结果如下:
The object will be destructed
Notice: Undefined variable: person in /Users/zhangshilei/PhpstormProjects/untitled/demo/Person.php on line 19 Notice: Trying to get property of non-object in /Users/zhangshilei/PhpstormProjects/untitled/demo/Person.php on line 19
*/PHP构造函数和析构函数
- __get()和__set()方法,这两个方法主要是实现了可以在类的外部通过对象直接访问类的私有属性,还可以增加类中没有定义的属性,如给$person对象增加marriage属性,只需$person->marriage="married"即可。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __get($field) {
// TODO: Implement __get() method.
echo "the get method is executed<br>";
return $this->$field;
}
public function __set($key, $value) {
// TODO: Implement __set() method.
echo "The set method is executed<br>";
$this->$key = $value;
}
} $person = new Person();
$person->name="Lisi";
echo $person->name; echo "<br>";
$person->age = 25; //age作为$person的私有属性,如果没有__set()方法此句会报错
echo $person->age; //没有__get()方法,此句会报错,如果__get()方法中没有return语句,该句没有返回值 /*输出结果如下:
Lisi
The set method is executed
the get method is executed
25
结果解释如下:
不知道大家注意到了没有,设置公有属性name的值没有调用__set()方法,读取公有属性name的值没有调用__get()方法
只有在设置和读取私有属性age的值才调用了__get()和__set()方法
*/__get()和__set()方法
- __call()和__callStatic():当类对象调用的方法不存在而且类中定义了__call()方法时,则会自动调用类的__call(),当类调用静态方法不存在而且类中定义了方法__callStatic()时,则会自动调用__callStatic()方法。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __call($name, $arguments) {
// TODO: Implement __call() method.
echo "被调用方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
public static function __callStatic($name, $arguments) {
// TODO: Implement __callStatic() method.
echo "被调用静态方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
} $person = new Person();
$person->shopping("clothes","fruit","snack");
Person::travel("Beijing","Pair","London","Prussia"); /*
输出结果为:
被调用方法的名称为:shopping
传入该方法的参数为:array(3) { [0]=> string(7) "clothes" [1]=> string(5) "fruit" [2]=> string(5) "snack" }
被调用静态方法的名称为:travel
传入该方法的参数为:array(4) { [0]=> string(7) "Beijing" [1]=> string(4) "Pair" [2]=> string(6) "London" [3]=> string(7) "Prussia" }
*/__call()和__callStatic()方法
- __toString()方法,该方法主要用于格式化打印一个对象,该方法的设计原型来源于Java。只有类中实现了__toString()方法才可以通过echo直接打印该对象。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __toString() {
// TODO: Implement __toString() method.
return "My name is $this->name.<br> I am $this->age years old and I am a $this->sex.";
}
} $person = new Person();
echo $person;
/*
输出结果如下:
My name is zhangsan.
I am 23 years old and I am a male.
*/__toString()
关于PHP的魔术方法我们就简单介绍到这里,由上我们可以看出从构造方法上,PHP相比于还稍有欠缺,但PHP中有__set()和__get(),使得动态增加对象的属性字段变得更加方便,而对于Java来说要实现类似的效果,就不得不借助反射API或直接修改编译后字节码的方式实现了。Java中有反射机制,那么PHP中呢?接下来让我们来看一看PHP中的反射实现。
PHP中的反射机制
反射,直观的理解就是根据到达地找到出发地和来源。比如给出一个对象就可以找到对象所属的类、拥有哪些方法。反射可以在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,这种动态获取信息以及动态调用对象方法的功能称为反射API。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __sleep() {
// TODO: Implement __sleep() method
return array("name","age"); //该方法指定仅序列化对象的name和age属性
}
public function __wakeup() {
// TODO: Implement __wakeup() method.
var_dump($this->name); //打印输出对象的name属性的值
var_dump($this->sex); //打印输出对象的sex属性的值,由于sex没有被序列化,因此输出null
}
public function __destruct() {
// TODO: Implement __destruct() method.
echo "The object will be destructed";
}
public function __get($field) {
// TODO: Implement __get() method.
echo "the get method is executed<br>";
return $this->$field;
}
public function __set($key, $value) {
// TODO: Implement __set() method.
echo "The set method is executed<br>";
$this->$key = $value;
}
public function __call($name, $arguments) {
// TODO: Implement __call() method.
echo "被调用方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
public static function __callStatic($name, $arguments) {
// TODO: Implement __callStatic() method.
echo "被调用方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
public function __toString() {
// TODO: Implement __toString() method.
return "My name is $this->name.<br> I am $this->age years old and I am a $this->sex.";
}
public function sayHello($other){
echo "My name is $this->name, nice to meet you $other";
}
} $person = new Person();
$reflect = new ReflectionObject($person);
$props = $reflect->getProperties(); //获取对象的属性列表(所有属性)
foreach ($props as $prop){ //打印类中属性列表
echo $prop->getName(),"\r";
}
echo "<br>";
$methods = $reflect->getMethods(); //获取类的方法列表
foreach ($methods as $method){
echo $method->getName(),"\r";
}
echo "<br>";
//返回对象属性的关联数组
var_dump(get_object_vars($person)); echo "<br>";
//返回类的公有属性的关联数组
var_dump(get_class_vars(get_class($person))); echo "<br>";
//获取类的方法名组成的数组
var_dump(get_class_methods(get_class($person))); /*输出结果如下:
name age sex information
__construct __sleep __wakeup __destruct __get __set __call __callStatic __toString sayHello
array(1) { ["name"]=> string(8) "zhangsan" }
array(2) { ["name"]=> NULL ["information"]=> string(21) "I come from the earth" }
array(10) { [0]=> string(11) "__construct" [1]=> string(7) "__sleep" [2]=> string(8) "__wakeup" [3]=> string(10) "__destruct" [4]=> string(5) "__get" [5]=> string(5) "__set" [6]=> string(6) "__call" [7]=> string(12) "__callStatic" [8]=> string(10) "__toString" [9]=> string(8) "sayHello" }
*/
PHP中的反射应用
如上代码中介绍的是通过对象获取类的方法和属性字段,而反射不仅仅可以用于类和对象,还可以用于函数、扩展模块、异常等。既然反射可以探知类的内部结构,那么就可以利用反射机制实现插件的功能,也可以利用反射机制实现动态代理。接下来举个简单的例子看看如何通过反射机制实现动态代理。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function sayHello($other){
echo "My name is $this->name, nice to meet you ".implode("",$other)."<br>";
}
}
class Dynamicproxy{
private $target;
public function __construct($className) { //为动态代理传入类名称,则自动生成类对象
$this->target = new $className();
}
public function __call($name, $arguments) {
// TODO: Implement __call() method.
$reflect = new ReflectionObject($this->target);
if($method = $reflect->getMethod($name)){ //获取名称为$name的类方法
if($method->isPublic() && !$method->isAbstract()){
echo "Before the method ".$method->getName()." we should do something<br>";
$method->invoke($this->target,$arguments);
echo "After the method ".$method->getName()." we should do something<br>";
}
}
}
}
$objProxy = new Dynamicproxy("Person");
$objProxy->sayHello("Janny"); /*
输出结果如下:
Before the method sayHello we should do something
My name is zhangsan, nice to meet you Janny
After the method sayHello we should do something
*/
PHP反射实现动态代理
如上的代码中真正实现sayHello()动作的是Person类中的sayHello()方法,而Dynamicproxy仅是一个代理类,其中并没有定义sayHello()方法,而是通过__call()方法动态调用类Person的sayHello()方法。在DynamicProxy类中可以做sayHello()方法的前后拦截,并且可以动态的改变类中的方法和属性。很多时候,善用反射可以保持代码的优雅和简洁,但反射也会破坏类的封装性,因为反射可以使本不应该暴露的方法或属性被强制暴露了出来。
PHP中的异常和错误
在语言级别通常有许多错误处理模式,但这些模式往往建立在约定俗称的基础上,也就是错误都是可预知的。不同的语言对异常和错误的定义也是不一样的,在PHP中,遇到任何自身错误都会触发一个错误,而不是抛出异常。也就是说PHP一旦遇到非正常代码,通常都会触发错误,而不是抛出异常。因此如果想使用异常处理不可预料的问题,是办不到的。比如,想在文件不存在或数据库无法建立连接时触发异常,是不可行的。PHP会把这些作为错误抛出,而不是作为异常捕获。还是回到PHP的错误处理上,PHP中的错误级别大致分为以下几类:
- 最高级别的错误是语法解析错误 prase error。该错误属于语法检查阶段错误,这会导致PHP代码无法通过语法检查。
- 次之的错误是fetal error。该类错误会导致PHP流程终止,其后的代码无法继续执行。
- warning是警告级别的错误,在语法中出现很不恰当的情况才会报此错误,如参数不匹配、除数为0等。这种错误会导致不可预期的结果。
- notice是通知级别的错误,这种错误是在如变量使用前未定义、数组索引是字符时没有加引号等情况。
- 最低级别的错误是deprecated的错误,表示不推荐,不建议。如在PHP5中使用ereg系列的正则匹配函数就会报此类错误,该错误是由于使用了不推荐、过时的函数或语法造成的,不影响PHP正常流程。
function showError(){
$date = '2017-06-05';
if(ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})",$date,$regs)){ //deprecated级别错误
echo $regs[3].$regs[2].$regs[1];
}else{
echo "Invalid date format: $date";
}
if($value > 5){ //notice级别的错误
echo "It is amazing, the variable $value is not init",PHP_EOL;
}
$a = array('o'=>2,4,6,8);
echo $a[o]; //notice级别的错误
$sum = array_sum($a,3); //warning级别的错误,传入参数不正确
echo fun(); //fetal error
echo "the code is after fetal error"; //该句不执行
//echo "prase error:",$55;
}
showError();PHP中的Error
接下来我们看一看针对上边介绍的各个级别的错误PHP是如何处理的。PHP中提供了set_error_handler()函数来处理错误,当然该函数也不是可以托管所有种类的错误,如E_ERROR、E_PARSE、E_CORE_ERROR等错误,这些错误会以原始的方式显示。当然也可以通过restore_error_handler()取消接管:
function DivisionError($errno, $errmsg, $errfile, $errline){
// 获取当前错误时间
$dt = date("Y-m-d H:i:s (T)");
$errortype = array (
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parsing Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
); $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);
$err = "错误时间".$dt."<br>";
$err .= "错误等级".$errno ."<br>";
$err .= "错误等级名称" . $errortype[$errno] . "<br>";
$err .= "错误消息" . $errmsg . "<br>";
$err .= "错误发生所在文件" . $errfile . "<br>";
$err .= "错误所在行" . $errline . "<br>";
echo $err;
}
set_error_handler("DivisionError"); //当抛出错误时,直接交给DivisionError处理 function showError(){
$date = '2017-06-05';
if(ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})",$date,$regs)){ //deprecated级别错误
echo $regs[1].$regs[2].$regs[3];
}else{
echo "Invalid date format: $date";
}
if($value > 5){ //notice级别的错误
echo "It is amazing, the variable $value is not init",PHP_EOL;
}
$a = array('o'=>2,4,6,8);
echo $a[o]; //notice级别的错误
$sum = array_sum($a,3);
echo fun(); //fetal error
echo "the code is after fetal error"; //该句不执行
//echo "prase error:",$55;
}
showError();
PHP错误处理
如上这种“曲折迂回”的处理方式也存在问题:必须依靠程序员自己来掌控对异常的处理,对于异常高发区、敏感区,如果处理不好就会出现业务数据不一致的问题,但是优点就是可以获得程序运行的上下文信息,以进行针对性补救。
对于代码中存在的异常,需要认为的进行抛出,接下来我们通过自定义一个异常类来处理抛出的异常,
class DivisionException extends Exception{ //自定义异常处理类 public function __toString(){ //覆写父类的__toString(),规定输出格式
$errorMessage = '错误发生于文件'.$this->getFile().'第'.$this->getLine().'行<br>'
.'错误原因为'.$this->getMessage();
return $errorMessage;
}
}
class Calculate {
private $num1;
private $num2;
private $operater; public function __construct($num1, $num2, $operater) {
$this->operater = $operater;
$this->num1 = $num1;
$this->num2 = $num2;
} public function getResult() {
try {
if ($this->operater == "+") {
return $this->num1 + $this->num2;
} else if ($this->operater == "-") {
return $this->num1 - $this->num2;
} else if ($this->operater == "*") {
return $this->num1 * $this->num2;
} else {
if($this->num2 == 0){ //如果除数为0则抛出异常
throw new DivisionException("除数不能为0");
}
return $this->num1 / $this->num2;
}
} catch (DivisionException $exception){
echo $exception;
}
}
}
$calculate = new Calculate(10,0,"/");
echo $calculate->getResult();
/**
输出结果如下:
错误发生于文件/Users/zhangshilei/PhpstormProjects/untitled/demo/Person.php第98行
错误原因为除数不能为0
*/
PHP自定义异常处理类
初体验就为大家介绍到这里吧,以后有机会在深入的去了解PHP函数、PHP与网络、PHP与数据库等等的内容吧。
PHP初体验的更多相关文章
- .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...
- Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验
Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...
- Spring之初体验
Spring之初体验 Spring是一个轻量级的Java Web开发框架,以IoC(Inverse of Control 控制反转)和 ...
- Xamarin.iOS开发初体验
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAA+CAIAAAA5/WfHAAAJrklEQVR4nO2c/VdTRxrH+wfdU84pW0
- 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...
- 【Knockout.js 学习体验之旅】(1)ko初体验
前言 什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular.React啊,再不赶紧的话,他们也要变out了哦.身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包, ...
- 在同一个硬盘上安装多个 Linux 发行版及 Fedora 21 、Fedora 22 初体验
在同一个硬盘上安装多个 Linux 发行版 以前对多个 Linux 发行版的折腾主要是在虚拟机上完成.我的桌面电脑性能比较强大,玩玩虚拟机没啥问题,但是笔记本电脑就不行了.要在我的笔记本电脑上折腾多个 ...
- 百度EChart3初体验
由于项目需要在首页搞一个订单数量的走势图,经过多方查找,体验,感觉ECharts不错,封装的很细,我们只需要看自己需要那种类型的图表,搞定好自己的json数据就OK.至于说如何体现出来,官网的教程很详 ...
- Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验
Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出E ...
- Docker初体验
## Docker初体验 安装 因为我用的是mac,所以安装很简单,下载dmg下来之后拖拽安装即可完成. 需要注意的就是由于之前的docker是基于linux开发,不支持mac,所以就出现了docke ...
随机推荐
- css系列(布局):实现一个元素在浏览器中水平、垂直居中的几个方案
在开发中偶遇需要一个元素垂直居中的需求,之前都是水平居中,垂直居中使用的比较少,经过一通研究,选择了几种相对比较实用的方案分享,抛砖引玉,如有遗漏不足,还望不吝指正. 方案一(IE7下该方案无法实现垂 ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- JS判断值是否是正数
1.使用isNaN()函数 isNaN()的缺点就在于 null.空格以及空串会被按照0来处理 NaN: Not a Number /** *判断是否是数字 * **/ function isReal ...
- NodeJS 中npm包管理工具
NPM 使用介绍 NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种: 允许用户从NPM服务器下载别人编写的第三方包到本地使用. 允许用户从 ...
- python编程总结
1.signal.signal(signal.SIGCHLD,signal.SIG_IGN) 这句话的作用是 防止产生僵尸进程.详细解释看 http://www.cnblogs.com/Anker/p ...
- 七牛整合 ueditor (拦住那头牛,七牛又如何)
最近遇到个项目,要求所有图片都必须整合到七牛上,看了把你谈文档踩在前辈们的基础上终于把他完成了,恰巧本屌丝最近刚好有时间,本着天下屌丝是一家的原则,和小朋友们一同学习 闲话少说入正题. 第一 :下载编 ...
- 一起来学jquery!
jquery学习之旅!(未完待续) ------------(一)jquery书写步骤 ------------(二)jquery事件与函数 ------------(三)jquery修改css属性 ...
- Web常见约定规范(精选)
常见的约定规范 (一)HTML约定规范 1,html属性顺序:id class name data-xxx (src for type href)(title alt)(aria-xxx role) ...
- Java static 关键字详解
引言 在<Java编程思想>中有这样一段话:static方法就是没有this的方法.在static方法内部不能调用非静态方法,反过来是可以的.而且可以在没有创建任何对象的前提下,仅仅通过类 ...
- js获取一组不重复的随机数的方法
一.基本思路: 建立一个数组存放所有可以取到的值,每次从该数组中随机取走一个,放到新的数组中,直到完成. 二.实现方法 1.方法一: (1)创建一个数组arr,数组元素为所有可能出现元素的集合: (2 ...