1.javacsript标识符命名规则:与java相同。

2.变量
javascript是一种弱类型语言(java是一种强类型语言),变量可以保存任何类型的数据,每个变量只不过时一个用于保存任意值的命名占位符。。
声明:var 变量名;
赋值:变量名=值;
注意:一个变量默认值为undefined.
示例如下:
在这里插入图片描述
在这里插入图片描述
注意:一个变量还没有声明就使用会报错,js代码查看错误在浏览器中,快捷键为fn+f12,选择控制台窗口。
在这里插入图片描述

在这里插入图片描述
JS中数组的定义:
var arr =[false,true,1,2,“abc”,3.14];//数组中的数据类型可以不一致,注意不要写成var[] arr = {}的形式。
注意:虽然JS数组定义时没有使用[]指定大小,但仍可以使用[]里加下标的形式访问数组中元素。
遍历数组:
for( var i=0;i<arr.length;i++){
alert(arr[i]);
}
或者
for ( var i in arr){
alert(i);
}

3.函数(返回值也不区分类型)
3.1语法格式
声明
function 函数名(参数1,参数2,…,参数n){
执行地代码块
}
调用
函数名(实数1,实数2,…,实数n)
注意:(1)JS中的函数调用的实参个数可以少于形参个数。
(2)函数名是指向函数的指针。
(3)ECMAScript 6 的所有函数对象都会暴露一个只读的 name 属性,其中包含关于函数的信息。多数情况下,这个属性中保存的就是一个函数标识符,或者说是一个字符串化的变量名。即使函数没有名称,也会如实显示成空字符串。如果它是使用 Function 构造函数创建的,则会标识成"anonymous":

function foo() {} 
let bar = function() {}; 
let baz = () => {}; 
console.log(foo.name); // foo 
console.log(bar.name); // bar 
console.log(baz.name); // baz 
console.log((() => {}).name); //(空字符串)
console.log((new Function()).name); // anonymous 

如果函数是一个获取函数、设置函数,或者使用 bind()实例化,那么标识符前面会加上一个前缀:

function foo() {} 
console.log(foo.bind(null).name); // bound foo 
let dog = { 
 years: 1, 
 get age() { 
 return this.years; 
 }, 
 set age(newAge) { 
 this.years = newAge; 
 } 
} 
let propertyDescriptor = Object.getOwnPropertyDescriptor(dog, 'age'); 
console.log(propertyDescriptor.get.name); // get age 
console.log(propertyDescriptor.set.name); // set age 

特殊的匿名函数:
function (a,b)
{
console.log(a+b);
}
函数也是一种数据类型,属于Object对象类型中的一种,可以把匿名函数赋值给变量:
var fun = function (a,b)
{
console.log(a+b);
}
函数中有return语句即有返回值,alert(函数名)即在弹出窗口中显示返回值。
还有一种箭头函数(ECMAScript6新增了使用箭头=>语法定义函数表达式的能力),任何可以使用函数表达书的地方,都可以使用箭头函数。

let arrowSum = (a, b) => { 
 return a + b; 
}; 
let functionExpressionSum = function(a, b) { 
 return a + b; 
}; 
console.log(arrowSum(5, 8)); // 13 
console.log(functionExpressionSum(5, 8)); // 13 

注意:如果只有一个参数,那也可以不用括号。只有没有参数,或者多个参数的情况下,才需要使用括号。
箭头函数也可以不用大括号,但这样会改变函数的行为。使用大括号就说明包含“函数体”,可以在一个函数中包含多条语句,跟常规的函数一样。如果不使用大括号,那么箭头后面就只能有一行代码,比如一个赋值操作,或者一个表达式。而且,省略大括号会隐式返回这行代码的值:

// 以下两种写法都有效,而且返回相应的值
let double = (x) => { return 2 * x; }; 
let triple = (x) => 3 * x; 

函数的补充:在javascript函数体中,标识符arguments具有特殊含义。它是调用对象的一个属性,用来引用Arguments对象。Arguments对象就像一个数组,
(1)函数定义是可以没有形式参数,调用时又可以传递不同个数的实参。arguments可以看成保存函数实参的数组,length属性 表示函数实参个数,加上下表可以引用不同参数的值。
例如:通过arguments查找最大值
function findMax()
{
var max = arguments[0];
for(var i=1;i<arguments.length;i++)
{
if(max<arguments[i])max =arguments[i];
}
return max;
}
var max=findMax(12,22,9,21,6,29);
(2)arguments的callee属性,实现函数递归调用,其实arguments.callee与函数名同义。
例:
function fun(num)
{
if(num==1)
{return 1;}
else {return num*arguments.callee(num-1); }
}

3.2函数的参数
ECMAScript函数的参数与大多数语言不同,不关心传入的参数个数。例如,定义函数时要接收两个参数,并不意味着调用时就传两个参数,可以穿一个、三个,甚至一个也不传。原因是ECMAScript函数的参数在内部变现为一个数组,调用时也是接收一个数组。事实上,在使用function关键字定义(非箭头)函数时,可以在函数内部访问arguments对象,从中取得传进来的每个参数值。
例如:

 function add(num1,num2,num3){
            console.log("num="+num1+",num2="+num2+",num3="+num3);
        }
        add();
        add(1);
        add(1,2);
        add(1,2,3);

在这里插入图片描述

例如:

function sayHi(name, message) { 
 console.log("Hello " + name + ", " + message); 
} 

也可以写成如下形式:

function sayHi() { 
 console.log("Hello " + arguments[0] + ", " + arguments[1]); 
}

注意:如果函数是使用箭头语法定义的,那么传给函数的参数将不能使用 arguments 关键字访问,而只能通过定义的命名参数访问。

在这里插入代码片function foo() { 
 console.log(arguments[0]); 
} 
foo(5); // 5 
let bar = () => { 
 console.log(arguments[0]); 
}; 
bar(5); // ReferenceError: arguments is not defined

由于ECMAScript的函数参数的特性,它不支持函数重载。
3.3this指针
this指针在标准函数和箭头函数中有不同的行为。标准函数中表示调用该函数的上下文对象,箭头函数中表示定义该函数的上下文对象。
例如:

window.color = 'red';
let o = {
 color: 'blue'
};
function sayColor() {
 console.log(this.color);
}
sayColor(); // 'red'
o.sayColor = sayColor;
o.sayColor(); // 'blue'
window.color = 'red';
let o = {
 color: 'blue'
};
let sayColor = () => console.log(this.color);
sayColor(); // 'red'
o.sayColor = sayColor;
o.sayColor(); // 'red' 

3.4 caller
函数对象有一个属性caller,这个属性引用的是调用当前函数的函数,或者如果是在全局作用域中调用的则为 null

function outer() {
 inner();
}
function inner() {
 console.log(inner.caller);
}
outer(); 

上述代码输出outer的源代码:
在这里插入图片描述
3.5 new.target
ECMAScript 中的函数始终可以作为构造函数实例化一个新对象,也可以作为普通函数被调用。ECMAScript 6 新增了检测函数是否使用 new 关键字调用的 new.target 属性。如果函数是正常调用的,则 new.target 的值是 undefined;如果是使用 new 关键字调用的,则 new.target 将引用被调用的构造函数。

function King() {
 if (!new.target) {
 throw 'King must be instantiated using "new"'
 }
 console.log('King instantiated using "new"');
}
new King(); // King instantiated using "new"
King(); // Error: King must be instantiated using "new"

3.5 函数的属性(length、prototype)和方法(apply()、call()、bind())
ECMAScript 中的函数是对象,因此有属性和方法。
3.5.1 每个函数都有两个属性:length和 prototype。
其中,length 属性保存函数定义的命名参数的个数。

function sayName(name) {
 console.log(name);
}
function sum(num1, num2) {
 return num1 + num2;
}
function sayHi() {
 console.log("hi");
}
console.log(sayName.length); // 1
console.log(sum.length); // 

prototype 是保存引用类型所有实例方法的地方,这意味着 toString()、valueOf()等方法实际上都保存在 prototype 上,进而由所有实例共享。这个属性在自定义类型时特别重要。
3.5.2 方法(apply()、call()、bind())
apply()、call()这两个方法都会以指定的 this 值来调用函数,即会设置调用函数时函数体内 this 对象的值。apply()方法接收两个参数:函数内 this 的值和一个参数数组。第二个参数可以是 Array 的实例,但也可以是 arguments 对象。来看下面的例子:

function sum(num1, num2) {
 return num1 + num2;
}
function callSum1(num1, num2) {
 return sum.apply(this, arguments); // 传入 arguments 对象
}
function callSum2(num1, num2) {
 return sum.apply(this, [num1, num2]); // 传入数组
}
console.log(callSum1(10, 10)); // 20
console.log(callSum2(10, 10)); // 20 

在这个例子中,callSum1()会调用 sum()函数,将 this 作为函数体内的 this 值(这里等于window,因为是在全局作用域中调用的)传入,同时还传入了 arguments 对象。callSum2()也会调用 sum()函数,但会传入参数的数组。这两个函数都会执行并返回正确的结果。
call()方法与 apply()的作用一样,只是传参的形式不同。第一个参数跟 apply()一样,也是 this值,而剩下的要传给被调用函数的参数则是逐个传递的。换句话说,通过 call()向函数传参时,必须将参数一个一个地列出来,比如:

function sum(num1, num2) {
 return num1 + num2;
}
function callSum(num1, num2) {
 return sum.call(this, num1, num2);
}
console.log(callSum(10, 10)); // 20

apply()和 call()真正强大的地方并不是给函数传参,而是控制函数调用上下文即函数体内 this值的能力。考虑下面的例子:

window.color = 'red';
let o = {
 color: 'blue'
};
function sayColor() {
 console.log(this.color);
}
sayColor(); // red
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue 

ECMAScript 5 出于同样的目的定义了一个新方法:bind()。bind()方法会创建一个新的函数实例,其 this 值会被绑定到传给 bind()的对象。比如:

window.color = 'red';
var o = {
 color: 'blue'
};
function sayColor() {
 console.log(this.color);
}
let objectSayColor = sayColor.bind(o);
objectSayColor(); // blue 

这里,在 sayColor()上调用 bind()并传入对象 o 创建了一个新函数 objectSayColor()。objectSayColor()中的 this 值被设置为 o,因此直接调用这个函数,即使是在全局作用域中调用,也会返回字符串"blue"。

3.6 回调函数
3.6.1 回调函数的概念
把函数当作一个参数传到另外一个函数中,当需要用这个函数时,再回调运行这个函数。回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。

function add(num1,num2,callback)
{
var num = num1+num2;
callback(sum);
}
function print(num){
console.log(num);
}
add(1,2,print);//3

匿名回调函数:

function add(num1,num2,callback)
{
var num = num1+num2;
callback(sum);
}
add(1,2,function (sum){console.log(sum);});//3

注意:回调函数是一个闭包,即它能访问到其外层定义的变量。
3.6.2 回调函数中this的指向问题
注意在回调函数中调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。

function createData(callback){
callback();
}
var obj = {
data:100,
tool:function(){
createData(function(n){
console.log(this,111);
                      }
              }
}
obj.tool();//window 111

this指向的是离它最近的或嵌套级别的function的调用者。上例中调用function(n){console.log(this,111);的函数是createData,其所在的上下文为window。
解决回调函数的指向问题有两种方法:
(1)箭头函数

function createData(callback){
callback();
}
var obj = {
data:100,
tool:function(){
createData((n) =>{console.log(this,111); })
               }
}
obj.tool();//Object 111

在这里插入图片描述
使用箭头函数后,this指向是离它最近或嵌套级别的function的调用者,即obj。

function createData(callback){
callback(10);
}
var obj = {
data:100,
tool:function(){
createData((n) =>{this.data = n; })
               }
}
obj.tool(10);
console.log(obj.data);//10

4.作用域
全局变量定义在函数外。局部变量定义在函数内。注意,一个变量声明时没有var(当然也没有let,const),无论是在函数外还是函数内声明的都是全局变量。
注意。在JS中没有块级作用域。例如:
if(true){
var b=20;
}
console.log(b);
解释:JS中b是全局变量,会输出b,而在C,Java等语言中会报错。

5.数据类型
JS只定义了6中数据类型
null:空、无。表示不存在,当为对象的属性赋值为null,表示删除该属性

undefined:未定义。当使用var或let声明变量却没有赋值时会显示该值。可以为变量赋值为undefined

number:数值。最原始的数据类型,表达式计算的载体

string:字符串。最抽象的数据类型,信息传播的载体

boolean:布尔值,true和false两个值。最机械的数据类型,逻辑运算的载体

symbol:表示值为符号

说明:
(1)boolean:boolean类型的变量有两个字面值:true和false。这两个布尔值不同于数值,即true不等于1,false不等于0。注意,javascript区分大小写,所以True和False是有效的标识符,不是布尔值。
虽然布尔值只有两个,但是其他类型的值都有相应布尔值的等价形式。要将一个其他类型的值转换为布尔值,可以调用特定的Boolean转型函数。

let message = "Hello World";
let messageAsBoolean = Boolean(message);

Boolean转型函数可以把其他类型的变量转换为布尔值,即函数返回值为true或者false,转换规则取决于数据类型和实际的值。
转换规则如下:
string类型的变量若为非空字符串则转化为true,若为空字符串(“”)则转换为false;
number类型的变量若为非零数值(包括无穷值)则转化为true,若为0或NaN则转化为false;
object类型的变量若为任意对象则转换为true,若为null则转化为false;
undefined类型的变量转换为false。
注意:在控制流的判断语句中存在自动转换,例如

let message = "Hello World";
if(message){
console.log("Value is true");
}

上例中,console.log()会输出,因为字符串message会被自动转化为等价的布尔值true。
(2)NaN
有一个特殊的数值为NaN,意思是“不是数值”(Not a Number),用于表示本来要返回数值的操作失败了,而不是抛出错误。
NaN 有几个独特的属性。首先,任何涉及 NaN 的操作始终返回 NaN(如 NaN/10),在连续多步计算时这可能是个问题。其次,NaN 不等于包括 NaN 在内的任何值。例如,下面的比较操作会返回 false:
console.log(NaN == NaN); // false
为此,ECMAScript 提供了 isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”。把一个值传给 isNaN()后,该函数会尝试把它转换为数值。某些非数值的值可以直接转换成数值,如字符串"10"或布尔值。任何不能转换为数值的值都会导致这个函数返回true。举例如下:
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false,10 是数值
console.log(isNaN(“10”)); // false,可以转换为数值 10
console.log(isNaN(“blue”)); // true,不可以转换为数值
console.log(isNaN(true)); // false,可以转换为数值 1
上述的例子测试了 5 个不同的值。首先测试的是 NaN 本身,显然会返回 true。接着测试了数值 10和字符串"10",都返回 false,因为它们的数值都是 10。字符串"blue"不能转换为数值,因此函数返回 true。布尔值 true 可以转换为数值 1,因此返回 false。
(3)string数据类型
string数据类型可以使用双引号("),单引号(')或反引号(`)标示,因此下面的代码都是合法的:

let fireName = "John";
let fireName = 'John';
let fireName = `John`;

在字符串中对变量值进行引用使用$符号,例如

let a =6;
let b=9;
let c = "${a} + ${b} = ${a+b}";
console.log(c);//"6+9=15"

(4)symbol类型

补充:typeof操作符可以判断一个变量的数据类型,对一个变量使用typeof操作符会返回下列字符串之一:
“undefined”,“boolean”,“string”,“number”,“object”,“function”,“symbol”。
注意:(1)“typeof null"的返回值是"object”。
(2)对于还未声明的变量,使用typeof操作符返回的值是"undefined"。所以在实际开发中,对定义的变量最好赋值,这样就可以使用typeof操作符来判断一个变量是否被定义(判断的依据是typeof操作符执行的语句的返回值是否为"undefined")。
例如:

let message = "some string";
console.log(typeof message);//"string"
console.log(typeof (message));//"string"
console.log(typeof 95);//"number"

另外还有一种数据类型为object,即对象。

alert(typeof 数据 );语句输出数据的类型。
在这里插入图片描述

6.三种声明
var a=1;
let a=1;
const a=1;(常量声明)
区别:
(1)var
使用var定义的变量会成为包含它的函数的局部变量,即var声明的范围是函数作用域;
var声明的对象会自动加到window的对象上,称为window对象的属性,而let声明的变量不会;
var具有声明提升的机制,使用这个关键字声明的变量会自动提升到函数作用域顶部,例如

function foo(){
console.log(age);
var age = 26;
}
foo();
上述代码之所以不会报错,是因为在运行时把它看成等价于如下代码;
function foo(){
var age = 26;
console.log(age);
}
foo();
这就是所谓的“提升”,也就是把所有变量声明都拉到函数作用域的顶部;
反复多次使用var声明同一个变量不报错,例如:

function foo(){
var age = 16;
var age = 26;
var age = 36;
console.log(age);
}
foo();//36

(2)let
let声明遵循块作用域,即使用范围不能超出{ };
不能重复申明可以赋值,也可以不赋值;
必须先声明再使用,即没有“声明提升”;
不会变成window的属性;
举个例子:
在let出现之前,for循环定义的迭代变量会渗透到循环体外部:

  for(var i=0;i<5;i++)
   {
   //循环逻辑
   }
   console.log(i);//5

改变使用let之后,这个问题就消失了,因为迭代变量的作用域仅限于for循环的内部:

for(let i=0;i<5;i++)
   {
   //循环逻辑
   }
   console.log(i);//ReferenceError,i没有定义

(3)const
const 是声明时必须赋值,且不能再改的方式。
总结:在实际开发过程中最好遵循“不适用var,const优先,let次之”的原则。

7.Object类型
Object类型是所有类型的超类,自定义的任何类型默认继承Object.
Object包括prototype,constructor属性,toString(),valueOf(),toLocaleString()函数。在JS中自定义的类默认继承Object,会继承Object类中的所有的属性和函数。

8.类和对象
8.1创建对象的方式
(1)创建自定义对象的常用方法是创建一个新实例,然后给它添加属性和方法。如下例,

let person = new Object();
person.name = "Nicholas";
person.age=29;
person.sayName = function(){
console.log(this.name);//this.name会解析成person.name
};

(2)另外一种方法是使用对象字面量的形式,与使用Object(对象是等价的:

let person = {
name:"Nicholas",
age:29,
sayName(){
console.log(this.name);
}/* 等价于 sayName:function(){console.log(this.name);}*/
};

(3)定义构造方法,new对象
定义类的语法(与定义函数类似):
第一种方式
function 类名(形参)
{
}
第二种方式
类名 = function(形参)
{
}
创建对象的语法:
new 类名(实参);
注意:JS中也可以有语句,所以导致类和函数无法在定义时区分,但在调用时可以区分,即有new为对象,无new为函数(任何使用new操作符调用就是构造函数,不适用new操作符调用就是普通函数),而且不同的定义在一个 文件中无冲突。
在这里插入图片描述
类定义的形参就是它的属性,类的属性声明和调用在同时完成。
在这里插入图片描述
在这里插入图片描述
补充:普通函数创建的属性和方法添加到window对象中,
在这里插入图片描述
在这里插入图片描述

另一种类定义方式的用法:
在这里插入图片描述
(4)原型模式
每个函数都会创建一个prototype属性,指向原型对象(即这个属性就是一个对象)。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接付给对象实例的值,可以直接赋给它们的原型。

function Person() {} 
Person.prototype.name = "Nicholas"; 
Person.prototype.age = 29; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function() { 
 console.log(this.name); 
}; 
let person1 = new Person(); 
person1.sayName(); // "Nicholas" 
let person2 = new Person(); 
person2.sayName(); // "Nicholas" 
console.log(person1.sayName == person2.sayName); // true

使用函数表达式也可以:

let Person = function() {}; 
Person.prototype.name = "Nicholas"; 
Person.prototype.age = 29; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function() { 
 console.log(this.name); 
}; 
let person1 = new Person(); 
person1.sayName(); // "Nicholas" 
let person2 = new Person(); 
person2.sayName(); // "Nicholas" 
console.log(person1.sayName == person2.sayName); // true

因为从原型上搜索值的过程是动态的,所以即使实例在修改原型之前已经存在,任何时候对原型对象所做的修改也会在实例上反映出来。

补充:动态属性
例子:

let person = new Object();
person.name = "Nicholas";
console.log(person.name); // "Nicholas"

这里,首先创建了一个对象,并把它保存在变量 person 中。然后,给这个对象添加了一个名为name 的属性,并给这个属性赋值了一个字符串"Nicholas"。在此之后,就可以访问这个新属性,直到对象被销毁或属性被显式地删除。

9.for-in与for-of语句
(1)for-in语句
先补充in操作符,in操作符单独使用时,in操作符会在可以通过对象访问指定属性时返回true,无论属性是在实例上还是原型上。
for-in语句是一种严格的迭代语句,用于枚举对象中的非符号键属性。例如

for(const propName in window)
{document.write(proName);
}

上述例子中,使用for-in语句显示了一个包含4个元素的数组中的所有元素。循环会一直持续到将所有元素都迭代完。const不是必须的,为了确保这个局部变量不被改变,推荐使用const。因为对象的属性是无序的,for-in语句不能保证返回对象属性的顺序。
(2)for-of语句
for-in语句是一种严格的迭代语句,于遍历可迭代对象的元素。例如:

for(const el of [2,4,6,8])
{
document.write(el);
}

上述例子会显示数组中的所有元素。
(10)with语句
with 语句的用途是将代码作用域设置为特定的对象,其语法是:
with (expression) statement;
使用 with 语句的主要场景是针对一个对象反复操作,这时候将代码作用域设置为该对象能提供便利,如下面的例子所示:
let qs = location.search.substring(1);
let hostName = location.hostname;
let url = location.href;
上面代码中的每一行都用到了 location 对象。如果使用 with 语句,就可以少写一些代码:
with(location) {
let qs = search.substring(1);
let hostName = hostname;
let url = href;
}
这里,with 语句用于连接 location 对象。这意味着在这个语句内部,每个变量首先会被认为是一个局部变量。如果没有找到该局部变量,则会搜索 location 对象,看它是否有一个同名的属性。如果有,则该变量会被求值为 location 对象的属性。严格模式不允许使用 with 语句,否则会抛出错误。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐