PHP开发者的JavaScript快速教程(phper简明js教程)

本文提供word格式文档,下载链接: https://pan.baidu.com/s/1CcM2Dl4_rG0qe0omxPG8zQ 提取码: 9isx

前言:

每一位PHP开发者或多或少的都掌握一些JavaScript知识,本文写在《云客Drupal8源码分析》前端相关章节发布前,意在帮助沉浸在后端世界的phper快速进入前端js世界(推荐phper关注学习Drupal,那是php世界的珠峰,极其强大灵活的cms系统或者框架,被称web操作系统,非常优秀),但本文是独立的js教程,写作首要原则是全面,但每个知识点简明,将庞大的js知识压缩为一篇简明教程,如果需要js详细资料推荐阅读著名犀牛书《JavaScript权威指南》,可惜目前(20195月)该书尚未发布包含ES6的第七版。主要面向php开发者介绍目前常用的ES5.1版和ES62012年时所有现代浏览器均已支持ES5.1),足以支撑阅读代码和常用js操作,但并不介绍服务器端js相关内容,为方便记忆本文将对比和PHP语言的异同,更多参考资料在文尾列出。

概述:

js的语法标准由ECMA国际制定,官方地址为:http://www.ecma-international.org,该组织前身为:欧洲计算机制造商协会(European Computer Manufacturers Association),为体现开放、中立的国际组织性质,后改名为“ECMA国际”,因此该名称已不属于首字母缩写,ECMA国际维护很多标准,其中262号系列标准即为js语法标准,标准是随着时间发展的,262号标准包含了js已发布标准的所有版本,有个较重要的时间点:2015年6月,该月发布了《ECMAScript 2015 标准》,改动较大,目的是为让js支持大型软件开发,如服务器端脚本,此后每年六月发布新的小幅改进的标准,因此习惯上将2015年6月及以后的标准统称为ES6,记住ES6是一个泛指,并不是具体某年发布的特定版本,ES2016、ES2017相当于ES6.1、ES6.2等等,所有js标准的版本见:

http://www.ecma-international.org/publications/standards/Ecma-262-arch.htm

目前有不少工具可以将ES6标准的js转码为之前的版本标准。js不仅可以在浏览器中运行,也可以在服务器端像php脚本一样运行,甚至可以在桌面环境运行(见Rhino项目,可调用java的全部API),在不同环境运行有不同组成部分,但核心规范相同,如浏览器环境下由js核心、DOM、BOM组成,在服务器环境下,安装运行环境“node.js”即可,这里“node.js”是一个js的运行平台,类似php引擎,并不是服务器软件,“node.js”封装了谷歌V8引擎来执行js脚本;服务器端js运行时,无需专门的服务器软件,因此需要js脚本自行侦听端口,自行完成服务器功能,底层调用由“node.js”提供。

符号:

操作符基本和PHP一样,以下列出一些不一样或需要注意的符号:

加号“+ ”:

表示两个数相加或者连接两个字符串,在php中连接字符串是点号“.”;js加号的连接操作优先于相加运算,换句话说字符串和数字相加将是把数字转换成字符串再连接

引号:

在php中双引号会检测内部是否有变量,而js不管什么引号都不会检查变量,字符串在反引号中才能嵌入变量,推荐用单引号,她们区别并不大:双引号里面的字符串会经过编译器解释,然后再当作HTML代码输出;而单引号里面的不进行解释,直接输出

和号“&”:

和号在php中能使基本类型变量按引用传递,在js中无此用法,但都用来做位运算符

测试属性是否存在“in”:

示例:var yunke={a:1,b:2};if('b' in yunke){console.log('存在');},注意调试js往往用console.log();向浏览器控制台输出数据,本文将大量运用该方法

删除运算符“delete”:

类似php的unset(),删除可配置的对象属性或数组元素(见变量的可配置性),删除后为undefined(该值见后),删除数组元素后并不改变数组长度(length属性值),删除后可用in操作符测试

void:

对任何值返回undefined,如:<a href="javascript:void 'yunke';">Click me</a>,点击后没有任何动作

箭头函数“=>”:

箭头“=>”在php中用于数组,但ES6中js用来简写函数定义,见下

类型运算符“typeof”:

用在变量或字面值前面,以返回类型,如:console.log(typeof "me");,返回类型有:undefined、boolean 、number、string、function、object,其中object代表引用类型或 Null 类型(在js中null作为对象的占位符)

判断是否实例“instanceof”:

用typeof判断类型时可能会有点问题,可用该运算符补充,如:

        var a="yunke";
        var b= new String("yunke");
        console.log(typeof a); //向浏览器调试控制台输出string,按F12查看
        console.log(typeof b); //object
        console.log(a instanceof String);//false
        console.log(b instanceof String);//true

该运算符和原型链有关系,原型链见后

无符号向右移位“>>>”:

php没有该运算符,可用自定义函数代替

扩展运算符“…”:

这是在ES6中js新增的,叫扩展运算符,用在数组前面,将其展开形成逗号分隔的参数,后面可接表达式;通常用于函数调用,如:console.log(...[1, 2, 3])相当于console.log(1, 2, 3),除了调用也可以用于函数定义:

    function push(array, ...items) {
        array.push(...items);
    }
    var arr = [1, 2];
    push(arr, ...[3,4]);//注意不能省略“...”

在php中5.6版本及以上也有该运算符,叫做可变参数运算符,作用在以上情况时是类似的,但js中有更多功能,常见如下:

1、数组克隆与合并:

    const arr1 = ['a', 'b'];
    const arr2 = ['c'];
    const arr3 = ['d', 'e'];
    // ES5 的合并数组
    var arr = arr1.concat(arr2, arr3);
    // [ 'a', 'b', 'c', 'd', 'e' ]
    // ES6 的合并数组
    var arr = [...arr1, ...arr2, ...arr3];

2、与解构结合

3、将字符串转变为真正的数组(这可通过数组的.length属性判断字符长度,能正确识别非双字节的Unicode 字符,见下)

4、转化实现了 Iterator 接口的对象为数组(实际上扩展运算符背后就是调用遍历器接口)

在ES2018中将扩展运算符用到了对象上:

    let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
    x // 1
    y // 2
    z // { a: 3, b: 4 }

解构赋值时左侧扩展运算符只能放在最后一个参数上,以接收全部剩余可遍历属性,执行的是浅拷贝,仅列举可遍历属性,原型不被复制;在变量声明语句之中,如果使用解构赋值,扩展运算符后面必须是一个变量名,而不能是一个解构赋值表达式,其他用法和数组类似

以下操作符和php中的含义完全相同:

全等“===”、不全等“!==”、相等“==”、不等“!=”、大于“>”、大于等于“>=”、小于“<”、小于等于“<=”、且“&&”、或“||”、非“!”、自加“++”、自减“--”、求模“%”

指数运算“**”(又叫幂运算、指数运算,是ES6新增的)

算数运算符“+、-、*、/、%”,均可与等号连用(如a+=b;)

位运算符:且“&”、或“|”、互斥(xor)“^”、向左移位“<<”、有符号向右移位“>>”、取补数“~”

三目运算“(condition)?value1:value2”

逗号“,”从左到右连续执行表达式,如for (i = 0; i < 10; i++, j++){}

 

注释:

和PHP一样,多行使用/*内容*/  单行注释://

语句:

语句末尾的分号是可选的,如省略将以行末作为语句的结束,如果在一行中有多条语句,则语句间分号是必须的, php与此不同,其要求语句必有分号,和php一样JavaScript 会忽略多余的空格,因此可以使用空格格式化代码

关于换行您可以在文本字符串内部使用反斜杠对代码进行折行,如:

document.write("Hello \
World!")

但是不能像这样折行:

document.write \
("Hello World!")

保留关键字:

ES5.1关键词、保留词:

break、case、catch、continue、default、delete、do、else、finally、for、function、if、in、instanceof、new、return、switch、this、throw、try、typeof、var、void、while、with、abstract、boolean、byte、char、class、const、debugger、double、enum、export、extends、final、float、goto、implements、import、int、interface、long、native、package、private、protected、public、short、static、super、synchronized、throws、transient、volatile

ES6新增关键词、保留词:let、async、await

大小写:

JS变量名、常量名、函数名、类名均区分大小写,连布尔字面量、null、关键词都必须小写

PHP变量名、常量名区分大小写,函数名、类名、布尔字面量、null、关键词不区分大小写。

变量:

js变量名必须以字母、下划线或$开始,不能使用保留关键字。

声明变量:

var a;  var a=5;  var a=1, b=2;

这里var是variable(变量)的缩写,和php一样都是弱类型语言,无需声明数据类型;在相同作用域中再次通过var声明变量时,其值不会被重置或清除,除非声明的同时赋值;在全局作用域中通过var声明的变量无法用delete删除;在不同作用域中通过var声明变量代表声明不同的变量(即使变量名相同也是不一样的变量),不论在哪个作用域中,如果向未声明的变量赋值将在顶级作用域(全局作用域)隐式自动声明该变量。

在一个作用域中声明变量时,声明会被隐式提前到该作用域的开始,这样一来,在该作用域中声明的变量始终可见,这称为声明提前,是在预编译阶段执行的,见:

    var yunke = 'yunke';
    function test() {
        console.log(yunke); //输出undefined,而不是全局变量中的值,因为进行了声明提前
        var yunke;
        yunke = 'me';
        console.log(yunke); //输出me
    }
    test();

取用未经声明的变量会报错并终止脚本执行(typeof操作除外),这一点和php不同

动态变量名:

php允许动态变量名:

$a="yunke";
$$a=5;
echo $yunke;

但js无法像php那样直接支持,需如下:

    var yunke={a:'yunke',b:'is',c:'phper'};
    var varName='b';
    console.log(yunke[varName]);

将输出“is”,这里varName不能加引号,也不能采用点号方式

变量作用域:

js的作用域分全局作用域和函数作用域,函数作用域是局部的,里面的变量只能在函数内使用,由于函数内部还可以声明函数,内部声明的函数可用函数内变量,因此变量形成了作用域链,全局作用域是作用域链中的第一个节点,最外层的函数是第二个节点,依次到最里层的函数,最里层的函数作为作用域链的最后一个节点,在作用域链中,前面作用域中的变量在其所有后面的作用域中可见可用,反之则不行,换句话说,在外层作用域的变量自动在里层作用域可用,反之则不行,这点和PHP不一样,php需要用global关键词引进,js则直接使用;在使用变量时,依据作用域链向全局作用域方向依次查找,里层可以通过var声明同名变量以屏蔽外层变量;函数退出运行后没有被返回引用的变量会被注销,全局变量的生存期从声明它们开始,到页面关闭时结束;见代码:

<script type="text/javascript">
    var x = 0;
    function fun1() {
        var x = 1;
        function fun2() {
            var x = 2;
            function fun3() {
                console.log(x); //此处x值为2,用作用域链中找到的第一个变量x,最近作用域的值
            }
            fun3();
        }
        fun2();
    }
    fun1();
</script>

php中的函数是超全局的,只要被定义随处可使用,但js函数的作用域和变量相同,函数名可视为代表该函数的变量名,在上例中如果在fun1();后面执行fun3();将出错,因为在作用域链中找不到该函数,作用域链对理解闭包和with语句很重要(见后)。

在ES6中新增了let关键词来声明变量,和var用法类似,但有以下区别:

1、let声明的变量是块级作用域,一个“块”作用域即一个花括号,仅在块及块的内层子作用域中可用,这和var的函数作用域是很不一样的,通常用在循环中,示例如下:

    {
        let a = '块级变量';
        var b = '函数级变量';
    }
    console.log(a);//提示ReferenceError: a is not defined
    console.log(b);//正常输出

2、没有声明提前,必须在声明后使用,如提前使用将报错,这样也不行:

    var tmp = 123;
    if (true) {
        tmp = 'abc'; // 报错
        let tmp;
    }

3、let不允许在相同作用域内重复声明相同变量,这样也不行:

    function fun1() {
        let a = 1;
        var a = 2;
    }

4、在块作用域中使用let时必须有花括号,if (true) let x = 1;将出错,而应为:if (true) {let x = 1;}

5、let声明的全局变量,不再是window对象的属性:

    let a="yunke";
    console.log(window.a);//输出undefined

常量:

在ES6中新增关键词const来声明常量,如:const yunke = '我是云客';,重复声明或赋值常量将报错,作用域和let声明的变量相同,都是块级作用域,都不会声明提前;通过const声明的常量不是window对象的属性;当将一个对象赋值给常量时,常量保存的是这个对象的引用,常量意为保证引用不变,但对象是可以改变的,如:

    const foo = {};
    foo.prop = 123;// 为 foo 添加一个属性,可以成功
    console.log(foo);
    foo = {};//指向另外一个对象,将出错

数据类型:

js的数据类型分原始类型和引用类型,原始类型有五种:Undefined、Null、Boolean、Number 和 String。其他都是引用类型,又叫对象类型,部分类型介绍下:

undefined 类型:

该类型只有一个值,即undefined:

var yunke;
console.log(typeof yunke); //输出undefined
console.log( undefined === yunke);//输出true

注意比较时没有加引号,声明后未赋值、无返回值的函数将全等该值,值 undefined 实际上是从值 null 派生来的,在条件判断时都相当于false,和null的区别是:

console.log( undefined==null);相等而不全等,undefined是undefined类型,而null是object类型

含义上undefined 是声明了变量但未对其初始化时赋予该值,null 则用于表示尚未存在的对象

null是关键词,而undefined是一个预定义的全局变量

在js中只有null和undefined无法拥有方法和属性,在php中不存在undefined

数字:

js只有一种数字类型,不存在整数、浮点数一说,采用IEEE 754标准64位浮点格式储存,可以使用小数点,这可以表示很大的数(正负10的308次方),能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),极大或极小值用科学计数法表示,如5e2(500)、5e-1(0.5),这里e后面跟一个整数意为10的多少次方,支持二进制(前缀为0b)、8进制(前缀为0o)和16进制(前缀为0x),前缀可大写,尽管所有整数都可以表示为二、八或十六进制的字面量,但所有数学运算返回的都是十进制结果。

有个特殊的常量: NaN,表示非数(Not a Number),用于在类型(String、Boolean 等)转换失败时,例如,要把单词 blue 转换成数值就会失败,因为没有与之等价的数值,与无穷大一样,NaN 也不能用于算术计算,NaN 有个奇特之处是它与自身不相等,console.log(NaN == NaN);输出 false,用函数isNaN()来判断是否为非数字,isNaN("666")或isNaN(666)均返回false

数字的Num.toString(2)方法可以返回不同进制表示,不传递参数默认为10进制,常用进制有2/8/16

字符:

js采用utf-16编码的Unicode字符集,只有字符串类型,没有字符类型,字符串是由无符号的16位值组成的序列,最常用的Unicode字符都是通过16位内码表示,字符串长度是16位的个数(也就是有多少个双字节),如:

var str="云客";  console.log(str.length);//输出2

这将输出2,但并不是所有Unicode字符都是两字节,比如“?”(不是字母e,Unicode字符码为“\ud835\udc52”)使用了4字节,尽管是一个字符但其length将输出2,因为占用了两个16位组,类似的字符还有很多,比如中文“?”,详见Unicode编码规则,要正确得到字符串长度可用扩展运算符:[...str].length,该运算符是ES6添加的。

字符串赋值时可采用字面值方式,也可以采用Unicode字符码方式,如:

  var str="\ud835\udc52";  console.log(str);

但该方式只能表示\u0000~\uFFFF之间的字符,所以在ES6中可用大括号方式来表示超过此范围的字符,如:

var s = "\u{20BB7}";//输出“?”

当有大段字符串时,ES6可以使用反引号``(Esc下面那个键)包裹,里面可用${}来嵌入变量:

    var s = "yunke"; 
    var str=`我是${s},js用反引号嵌入大段字符串,里面的空格、换行等都会被保留`;

这称为标签模板,在反引号表示的字符串前可加一个函数名做标签,表示要用该函数来处理她,函数返回值作为表达式的值,如:alert`123` 等同于alert(123),当无嵌入变量时,字符串直接做函数的参数,当有嵌入变量时,会先将字符串处理成多个参数再调用函数(第一个参数是嵌入变量分隔的数组,后面参数是依次传递的嵌入变量值,${}中可以用任意表达式,传入的是其表达式值),注意:${}不用于单双引号,js没有提供在单双引号中嵌入变量的功能;

在js中字符串是只读的,可被当做数组,可以使用str[0]方式表示其中的字符,但以这种方式赋值是无效的(不抛出错误)

日期:

日期类型可以比较,如:

    var myDate = new Date();
    myDate.setFullYear(2008, 8, 9);
    var today = new Date();
    if (myDate > today) {
        console.log("今天在2008-9-9之前");
    }
    else {
        console.log("今天在2008-9-9之后");
    }

ES6新增的Symbol类型:

在ES5中只有5种原始类型和一种对象类型,Symbol类型是ES6新增的第七种类型,用来提供一个独一无二的标志,通常用于保证对象属性的唯一性,类似UUID,使用起来类似储存了一个UUID的变量,但该变量的类型是专用的symbol:

    let mySymbol = Symbol('yunke');
    console.log(typeof mySymbol); //输出“symbol”
    let a = {[mySymbol]: 'Hello!'};
    console.log(a[mySymbol]);

这里函数Symbol('yunke');返回一个symbol标志,其参数用于描述这个标志,相同参数调用多次时,尽管描述相同但每次返回的symbol都是不一样的,如果需要一样的可用Symbol.for('yunke');来返回上次用这个描述时返回的结果,symbol标志做属性时,只能用不带引号的方括号方式,不能用在点号后面,否则将被当做标识符而不是symbol标志,这表现的像变量名一样;函数Symbol();前不能使用new命令,否则报错。这是因为生成的 Symbol 是一个原始类型的值,类似字符串,不是对象;Symbol 值不能与其他类型的值进行运算,比如+连接等,可以显式转为字符串、布尔值,但是不能转为数值

Symbol 作为对象的属性名,该属性不会出现在for/in、for/of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,但它也不是私有属性,可用Object.getOwnPropertySymbols方法获取指定对象的所有 Symbol 属性名,要获得所有类型的键名,包括常规属性和 Symbol 属性可以用Reflect.ownKeys方法,除了自定义使用Symbol 值以外,ES6 定义了 11 个内置的 Symbol 值,指向语言内部使用的方法:

Symbol.hasInstance属性:

foo instanceof Foo运算将调用方法Foo[Symbol.hasInstance](foo)

Symbol.isConcatSpreadable属性:

布尔值,表示对象用于Array.prototype.concat()时,是否可以展开

Symbol.species属性:

指向一个构造函数。创建衍生对象时,会使用该属性

Symbol.match属性:

指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值

Symbol.replace属性:

指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值

Symbol.search属性:

指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值

Symbol.split属性:

指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值

Symbol.iterator属性:

指向该对象的默认遍历器方法

Symbol.toPrimitive属性:

指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值

Symbol.toStringTag属性:

指向一个方法。调用对象toString方法时,如果该属性存在,其返回值会出现在返回的字符串之中

Symbol.unscopables属性:

指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除

类型转换:

类型转换通常使用转换函数Boolean(value) 、Number(value)、String(value),这和php的(int)value方式不同,对象转化为原始类型时较复杂,通常是调用对象上的一个方法,转化为字符串用toString(),转化为数字用valueof(),所有对象转化为布尔将是true,即便是代表false的布尔对象也将转化为true,条件比较时特别要注意这一点,有些操作符也会进行隐式转换,如:+、!等

函数:

普通方式(声明方式):

<script type="text/javascript">
function name()
{
alert("Hello World!")
}
</script>

普通方式也叫函数声明方式,其定义的函数可以先调用后定义,也就是所谓的声明提前,提前时如果函数内用了在调用处尚未初始化的变量,则变量值为undefined;js函数可重新声明,此时以重新声明的为准,这一点和php不同,更不同的是函数其实是一个对象,它本身可以拥有属性和方法,还可以被当做参数传递:

    function yunke() {
    }
    yunke.a=88;
    yunke.f=function(){this.a=66;};
    yunke.f();
    console.log(yunke);
    console.log(yunke.a);

所有函数都是Function的实例:

    var yunke = new Function("iNum", "console.log(iNum + 10)");//最后一个参数做函数体,前面的做函数参数
    var php = yunke; //同一个函数可以有很多函数名
    yunke(10);	//输出 "20"
    php(10);	//输出 "20"

虽然函数是对象但typeof操作符返回function而不是object

表达式方式:

    var yunke = function () {
        console.log('yunke1');
    }
    yunke();

这种方式定义的函数只是变量名被声明提前,函数体并没有;表达式方式的函数名是可选的,存在时在递归操作中很有用,如:

var yunke = function yk(num) {return num<1?yk(num-1):console.log('递归减1成功');};

此时函数名是函数体内的一个局部变量,此列中yk仅函数体中有定义,如果是声明方式(非表达式方式)时,函数名就不仅在函数体内有效,只要在该函数的作用域中都有效。

函数可以匿名定义并立即运行:

    (function () {
        console.log('yunke');
    })();

JS大小写敏感,关键词function如果大写就会出错,函数名同样大小写敏感,函数内部还可以声明函数,但内部声明的函数是局部的,外部不可使用,这一点和php不一样,php函数是超全局的;不要在if语句或循环语句中采用声明方式声明函数(表达式方式不受限制),虽然有些实现支持,但这样做代码移植性不好。

函数内部可以使用关键词this,分情况含义不同,当函数挂载到对象上,就是对象的

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值