前端(三):JavaScript基础

JavaScript是一种属于网络的脚本语言,常用来为网页添加各式各样的动态功能,是一种动态类型、弱类型、基于原型的语言。
它包括三个部分:ECMAScriptBOMDOMECMAScript描述了该语言的语法和基本对象。BOM,浏览器对象模型,描述了与浏览器进行交互的方法和接口。DOM,文档对象模型,描述了处理网页内容的方法和接口。
js的使用:js代码写在script标签中,其引入方式和css样式的style标签一致。

一、变量、数据类型与运算符

  1.变量

  声明变量

- 通过var关键字声明一个或多个变量。
  - 声明变量的同时可以给其赋值(直接量,字面量),也可以不赋值,不赋值时默认值为undefined
  - 变量命名遵循一般的命名规范:区分大小写,不能包含特殊字符,大小驼峰命名或下划线命名,不能以数字开头,命名含义明确等。
  - 变量在内存中会自动存储于释放。

  2.数据类型

js中的数据类型包括:字符串(String)、数字(Number)、布尔(Boolean)、数组(Array)、对象(Object)、空(Null)、未定义(Undefined)。

  隐式转换

// 转换为布尔类型
var a; // undefined.
var test = function(vec){
if(vec){
console.log("true");
}else{
console.log("false");
}
};
a = null;
a = 0; // 0
a = NaN; // NaN
a = ''; // 空字符串
test(a);
// 转换成数值型数据
var a;// undefined -> NaN
var b = 1;
a = null; // null -> 0
a = false; // false -> 0
a = "123"; // "12" -> 12
a = "123abc"; // "123abc" -> NaN
console.log(typeof a);
console.log(b + a);

  强制转换

// parseInt(string):将字符串类型转换成数值;
// parseFloat(string):将字符串类型转换成浮点型数值;
var a = "123";
console.log(typeof parseInt(a));
console.log(typeof parseFloat(a));
// String()toString():将其它类型转换成字符串
var a = 123;
console.log(typeof String(a));
console.log(typeof a.toString());
// Boolean:将其它数据类型转换成布尔值。;
var a = undefined;
console.log(Boolean(a), typeof Boolean(a));

  3.运算符

  1.算术运算符

+-*/%++--
++--分为前缀形式和后缀形式。前缀先自加()再执行运算,后缀先执行运算再自加()

  2.赋值运算符和比较运算符

=+=-=*=/=%=.=
>>=<<===!====!==

  3.逻辑运算符

&&||!

  4.三元运算符

// exp1?exp2:exp3
var a = 1231; // var a;
typeof a==="number"?console.log("true"):console.log("false");

  5.运算符优先级

运算符 描述
. [] () 字段访问、数组下标、函数调用以及表达式分组
++ -- - ~ ! delete new typeof void 一元运算符、返回数据类型、对象创建、未定义值
* / % 乘法、除法、取模
+ - + 加法、减法、字符串连接
<< >> >>> 移位
< <= > >= instanceof 小于、小于等于、大于、大于等于、instanceof
== != === !== 等于、不等于、严格相等、非严格相等
& 按位与
^ 按位异或
| 按位或
&& 逻辑与
|| 逻辑或
?: 条件
= oP= 赋值、运算赋值
, 多重求值

二、流程控制

  1.条件语句

// if(exp){执行代码段};
var a = 123; // var a;
if(typeof a === "number"){
console.log("true");
}
// if(exp){exptrue的代码段}else{expfalse的代码段};
var a = 123; // var a;
if(typeof a === "number"){
console.log("true");
}else {
console.log("false");
}
// if ... else if ...
var readline = require("readline");
var rl = readline.createInterface({
input: process.stdin,
out: process.stdout,
});
rl.question("请输入成绩: ", (number) => {
if(number > 100){
console.log("输入的成绩不能大于100");
}else if(number > 80){
console.log("录入成功,A");
}else if(number > 60){
console.log("录入成功,B");
}else if(number > 0){
console.log("录入成功,C");
}else{
console.log("输入的成绩不能小于0")
}
rl.close();
});
rl.on("close", function () {
console.log("退出程序!");
process.exit();
});
var readline = require("readline");
var rl = readline.createInterface({
    input: process.stdin,
    out: process.stdout,
});
rl.question("请输入星期几: ", (day) => {
    switch(day){
        case "1":
            console.log("今天吃炒面");
            break;
        case "2":
            console.log("今天吃鱼香肉丝");
            break;
        case "3":
            console.log("今天吃麻辣香锅盖饭");
            break;
        case "4":
            console.log("今天吃豆丁胡萝卜");
            break;
        case "5":
            console.log("今天公司聚餐");
            break;
        case "6":
            console.log("今天吃泡面");
            break;
        case "7":
            console.log("今天撸串");
            break;
        default:
            console.log("input error.")
    }

    rl.close();
});
rl.on("close", function () {
    process.exit();
}
// switch ... case ...
var readline = require("readline");
var rl = readline.createInterface({
input: process.stdin,
out: process.stdout,
});
rl.question("请输入星期几: ", (day) => {
switch(day){
case "1":
console.log("今天吃炒面");
break;
case "2":
console.log("今天吃鱼香肉丝");
break;
case "3":
console.log("今天吃麻辣香锅盖饭");
break;
case "4":
console.log("今天吃豆丁胡萝卜");
break;
case "5":
console.log("今天公司聚餐");
break;
case "6":
console.log("今天吃泡面");
break;
case "7":
console.log("今天撸串");
break;
default:
console.log("input error.")
}

rl.close();
});
rl.on("close", function () {
process.exit();
});

  2.循环语句

  for 循环

java中的for循环一致:for(exp1; exp2; exp3){代码块;}
  - exp1: 无条件的执行第一个表达式
  - exp2: 是判断是否能执行循环体的条件
  - exp3: 增量操作

// 九九乘法表
var str = "";
for(var i=1;i<=9;i++){
for(var j=1;j<=i;j++){
str += i + "*" + j + "=" + i*j + " ";
if(i === j){
str += "\n";
}
}
}
console.log(str);

  while 循环

var arr = function (number) {
    var arr_list = new Array(); // var arr_list = [];
    var i = 0;
    while (i < number){
        arr_list[i] = Math.random();
        i++;
    }
    return arr_list;
};
console.log(arr(5))
var arr = function (number) {
var arr_list = new Array(); // var arr_list = [];
var i = 0;
while (i < number){
arr_list[i] = Math.random();
i++;
}
return arr_list;
};
console.log(arr(5));

  do ... while循环

 
 
// java中的do ... while 循环一致,先执行依次do内的代码块,再执行while 判断。不管while条件判断是否成功,do都会至少执行一次。
var arr = function (number) {
var arr_list = new Array(); // var arr_list = [];
var i = 0;
do {
arr_list[i] = Math.random();
i++;
}while (i > number);
return arr_list;
};
console.log(arr(5));

三、函数

  1.定义

  函数通过function关键字定义。function 函数名称([参数1, ...]){代码块; [return 返回值]};也可以通过Function构造器定义函数。

 
 
// 通过function 关键字定义函数
function hello() {
console.log("hello world.")
};
hello();
 

  匿名函数,即function关键字定义的无函数名称的函数。

// 通过function 关键字定义匿名函数
var hello = function () {
console.log("hello world.")
};
hello();

  将匿名函数作为参数,传递给另一个函数进行执行,此时其被称为回调函数。回调函数在js中异常强大。

function calc(v1, v2, callback) {
v1 = v1 || 1;
v2 = v2 || 2;
return callback(v1, v2);
}
calc(3,4, function (v1, v2) {
return v1 + v2
});

  

  2.参数

  函数如果定义了参数,在调用函数的时候没有传值则默认值为undefined。如果传递的参数超过了形参,js会忽略掉多于的参数值。

function  calc(v1, v2){
return v1 + v2;
}
console.log(calc(5, 6, 7));

  es5不能直接写形参的默认值,但可以通过arguments对象来实现默认值。同时,arguments对象可以实现可变参数。

function  calc(v1, v2){
v1 = v1 || 1;
v2 = v2 || 2;
return v1 + v2;
}
console.log(calc());

  3.函数调用

function calc(v1, v2, callback) {
v1 = v1 || 1;
v2 = v2 || 2;
return callback(v1, v2);
}
value1 = calc(3,4, function (v1, v2) {
return v1 + v2
});

value2 = calc.call(calc, 3, 4, function (v1, v2) {
return v1 + v2;
});

value3 = calc.apply(calc, [3, 4, function (v1, v2) {
return v1 + v2;
}]);

value4 = (function (v1, v2) {
return v1 + v2;
})(3, 4);
console.group("函数调用的方式");
console.log("- 直接调用: " + value1);
console.log("- 系统call调用: " + value2);
console.log("- 系统apply调用: " + value3);
console.log("- 自调用: " + value4);

四、对象

  js中对象分为:内建对象、宿主对象和自定义对象。

  1.对象创建

  直接通过var关键字定义Object对象。

var obj1 = new Object();
var obj2 = {};
// 使用字面量来创建一个对象: var obj = {} new本质上是一模一样的
// 使用字面量创建对象时,可以在创建对象时直接指定对象中的属性
// 字面量里面的属性可以加引号,也可以不加引号,建议不加
// 如果使用特殊字符或名字则必须加引号
var obj = {
name: "孙悟空",
gender: "男猴",
age: 1500,
credit: {
name1: "孙行者",
name2: "齐天大圣"
}
};
console.log(obj);

  也可以通过工厂方法创建对象。

function Person(name, age) {
var obj = {};
obj.name = name;
obj.age = age;
obj.sayHello = function () {
console.log(obj.name + ", " + obj.age + "years old.");
};
return obj;
}
sun = Person("孙悟空", 2000);
sun.sayHello();

  2.对象属性操作和方法创建

 
 
var obj = new Object(); // new 构造对象, 可使用typeof obj 查看对象
// 在对象中保存的值称为属性
// 向对象中添加属性:对象.属性名 = 属性值
obj.name = "孙悟空";
obj.gender = "男猴";
obj.age = 18;
console.log(obj);
// 读取对象中的属性: 对象.属性名
// 如果读取的对象中没有该属性,会返回undefined
console.log(obj.name);
// 修改对象的属性值: 对象.属性名 = 新值
obj.age = 24;
console.log(obj);
// 删除对象属性
delete obj.age;
console.log(obj);
// 自定义
obj.fuc = function add(x, y) {
return x+y;
};
console.log(obj);
 

  3.作用域

js中一共有两种作用域,全局作用域和函数作用域
全局作用域:
- 直接编写在script标签内的JS代码
- 在页面打开时创建,在页面关闭时销毁
- 在全局作用域中有一个全局对象window,可以直接使用.它代表的是一个浏览器的窗口,它由浏览器创建
- 在全局作用域中创建的变量都会作为window对象的属性保存
- 在全局作用域中创建的函数都会作为window对象的方法保存
- 全局作用中的变量和函数在页面的任意位置都可以被访问/执行

  函数作用域:
  - 调用函数时创建函数作用域,函数执行完毕之后,函数作用域销毁
  - 每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的
  - 全局作用域中访问不到函数作用域,函数作用域中可以访问全局作用域
   - 函数作用域会优先查找本作用域中的变量,如果没有就找上一级中的变量
  - 函数作用域的a并不影响上一级作用域中的a
- 如果不使用var声明,直接写a = 20,就是在操作全局作用域中的a变量
- 如果使用全局作用域中的a变量[在本作用域中已声明a变量],可以用window.a,这在b1中已强调过

  全局作用域

console.log(window); // window是个对象

// 使用var关键字声明的变量,会在所有的代码执行之前执行;如果声明变量时不使用var,则变量不会被提前声明
console.log(a); // 此时的var a已被声明,它的值是undefined,到下一行时它的值才会是123

var a = 123; // 它相当于window.a = 123
console.log(window.a);

console.log(abc()); // 可以提前声明和执行
console.log(abc2()); // var提前声明的是一个undefinedwindow属性,不是个函数: abc2 is not a function

// 使用function关键字声明的函数,和var的机制一样,是函数声明的提前,它会提前(优先)执行
function abc() { // 它相当于window.abc = function (){console.log("abc")}
console.log("abc");
}
console.log(window.abc());

// 使用函数表达式不会被声明提前
var abc2 = function() {
console.log("abc2");
}

  局部作用域

var a = 19;
var b = 30;
function fun() {
a = 20;
var b = 1000;
console.log("a = " + a); // 这里打印的是20
console.log("b = " + b); // 这里打印的是1000
console.log("window.b = " + window.b); // 这里打印的是30
}
fun();
console.log(a); // 这里打印的是20
// 定义形参相当于在函数作用域中声明了变量

五、原型

  1.this关键字

解析器在调用函数时,会向函数内部传递进一个隐含的参数这个隐含的参数就是thisthis指向的是一个对象,这个对象称为函数执行的上下文对象
根据函数的调用方式不同,this会指向不同的对象
- 1.以函数的形式调用时,this永远都是window
- 2.以方法的形式调用时,this就是调用方法的那个对象
var name = "全局名字";
function func(a, b) {
console.log("a = " + a + ", b = " + b);
console.log(" Object: " + this);
console.log(this);
}
func(123, 456); // this指的是window
function func2() {
console.log(this.name);
console.log(this);
}
var obj = {
name: "孙悟空",
sayName: func, // 把函数赋给属性,this指的是obj
sayName2: func2

};
var obj2 = {
name: "猪八戒",
sayName: func, // this指的是obj2
sayName2: func2
};
obj2.sayName(2332, 4523);
obj.sayName(234, 789);
obj.sayName2(); // this可以支持对象内部的函数去访问对象内部的属性
obj2.sayName2(); // 这一点特别像python类中的self
 
 
var obj3 = {
name: "沙和尚",
teacher: "唐僧",
address: "流沙河",
sayMyTeacher: function3,
sayMySelf: function2,
say: function1
};
function function1() {
var say = this.sayMySelf() + this.sayMyTeacher();
console.log(say);
}
function function2() {
var say = "大家好,我是" + this.name + ",我老家是" + this.address;
return say
}
function function3() {
var say = "。我的师父是" + this.teacher + "老师, 他是个得道高僧";
return say
}
obj3.say();

  2.构造方法的重写

 
 
function Person1(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
// this.sayName = function() {
// console.log("大家好,我系" + this.name); // 这样写每创建一个对象,就会创建一个sayName
// }
this.sayName = sayName; // 共用同一个方法,它就相当于python中的类方法[类方法只有一份,但每个实例对象都共用]
} // 注意这里的写法,一个隐含的this传递给了sayName函数
var per1 = new Person1("孙悟空", 18, "");
per1.sayName();

// 在全局作用域中写sayName
// 但是它污染了全局作用域的命名空间,也就是全局作用域不能再写sayName函数
// 另外这个函数也很不安全,由此引出了"原型"的概念
function sayName() {
console.log("大家好,我系" + this.name);
}

  3.原型

  1.原型prototype


我们所创建的每一个函数,解析器都会向函数中添加一个属性:prototype
如果函数作为普通函数调用prototype,没有任何作用
当该函数以构造函数的形式调用[per1]时,它[per1]会有一个隐含的属性__proto__指向其原型对象[Person]
每次调用时都会有各自的__proto__指向原型对象的prototype也就是原型对象中的属性和方法被调用函数"共享"
function Person() {}
console.log(Person.prototype);

var per1 = new Person();
console.log(per1.__proto__ == Person.prototype); // true

Person.prototype.a = "我是原型对象中的123456";
per1.a = "我是mc中的" +"123456";

var per2 = new Person();
console.log(per1.a); // 找到调用函数的属性和方法,直接执行
console.log(per2.a); // 如果调用函数没有该属性或方法,会从原型对象中寻找

Person.prototype.sayName = function() {
console.log('我是原型对象的sayName');
};
per2.sayName = function() {
console.log('我是per2中的sayName');
};
per1.sayName(); // 和前面的类似
per2.sayName(); // 和前面的类似 // 于是现在解决上页的一个全局作用域污染问题: 把对象的函数写在prototype里而不是全局作用域里function MyPerson(name, age, gender) { this.name = name; this.age = age; this.gender = gender; MyPerson.prototype.sayName = function() { // 在自己的prototype空间里写函数 console.log("大家好,我系" + this.name); // 让每个实例都能访问,这才是标准的类方法 }}var mp1 = new MyPerson("孙悟空", 2000, "男猴"); // 注意这里使用的是new MyPerson,构造mp1
mp1.sayName();
// 原型对象也有__proto__属性,也会指向原型对象的原型中的prototype
// 原型对象的顶层就是Object的对象: obj1.__proto__.__proto__.__proto__ ...

   2.原型模拟类和对象

  在python中声明一个类和对象:

class Person(object):
  def __init__(self, name, age):
    self.name = name
    self.age = age
  def sayHello(self):
    print("{}, {} years old.".format(self.name, self.age))

sun = Person("孙悟空", 2000)
sun.sayHello() # 对象可以调用方法,因为方法只有一份且存在类内存中,每个对象只保留了引用
Person.sayHello(sun) # 类可以传入一个对象来调用方法,因为方法存在类内存中

  在Java中创建一个类和对象:类的方法存在方法区,对象存在堆内存中,多个对象共用它们父类的方法。它需要设置静态方法来实现对象调用。

public class Person {
private String name;
private int age;
Person(String name, int age){
  this.name = name;
  this.age = age;
  }  
  public void sayHello(){
  System.out.println(this.name + ", " + this.age + "years old.");
  }
  public static void sayHello(Person obj){
  System.out.println(obj.name + ", " + obj.age + "years old.");
  }
  public static void main(String[] args){
  Person sun = new Person("孙悟空", 2000);
  Person.sayHello(sun);
  sun.sayHello();
  }
}

  在js中,对象保存在堆内存中,每创建一个新的对象都会开一个堆内存空间,并把其属性和方法都保存在堆内存中。注意,如果不用原型,每个对象都会将其方法复制一份到自己的堆内存空间中。js中实现类调用实例对象:

 
 
function MyPerson(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
MyPerson.sayName = MyPerson.prototype.sayName = function (obj) { // 在自己的prototype空间里写函数
obj = obj || this;
console.log("大家好,我系" + obj.name + ", 一只火眼金睛的" + obj.gender); // 让每个实例都能访问
}
}
var mp1 = new MyPerson("孙悟空", 2000, "男猴"); // 注意这里使用的是new MyPerson,构造
mp1.sayName();
MyPerson.sayName(mp1);

猜你喜欢

转载自www.cnblogs.com/kuaizifeng/p/9293408.html