js 随笔 1-回炉重造

0.该随笔灵感主要来源于阮总的开源博客

阮一峰 JavaScript 教程

主要涵盖了js发展历史中比较重要的语法,以及一部分ES5的内容

1.入门篇

1.1 for循环

js对象为对象类型时候,for-in 遍历的是其键名,for-of 将抛出异常:TypeError: obj is not iterable
js对象为数组类型时候,for-in 遍历的是其索引(string), for-of遍历其元素(可以是对象)
    准确的来是说,不仅仅是索引,这种方式甚至可以获取其原型链的属性

2.数据类型

2.1 对象

2.1.1 对象取值

js对象为对象类型,obj[‘键名’],注意必须带有’’,或者obj.键名

2.2 数组

2.2.1 in运算符

适用于对象,也适用于数组

var arr = [];
arr[2] = 'a';
2 in arr  // true
'2' in arr // true
1 in arr //false ,这里是数组的空位

2.2.2 数组的空位

当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)。
数组
空位的值被读取时候,会被看做undefined
使用delete arr[index]指令删除元素的值,将形成空位,并不会影响数组的length属性
数组的forEach()、for-in、Object.keys()将跳过空位的执行,如果某个位置值为undefined,将不会被跳过

2.2.3 类数组对象

如果一个对象的所有键名都是正整数或零,并且有length属性,称为“类似数组的对象”(array-like object)。

var obj = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};
obj[0] // 'a'
obj[1] // 'b'
obj.length // 3
obj.push('d') // TypeError: obj.push is not a function

2.2.4 slice()

slice()(切面)比substring()更加强大


数组的slice方法可以将“类似数组的对象”变成真正的数组。

Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]

可以使用Array.prototype.forEach等数组的原型方法,但是效率比原生数组慢

3.运算符

3.1 一些容易混淆的关键字

typeof运算符 判断对象的类型
instanceof运算符 用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上
in 运算符 无论该属性是自身的还是继承的,都为true

4.语法专题

4.1 数据类型的转换

4.1.1 Number类型的转换

运算自动转换、new (…)、parseFloat(…)

5.标准库

5.1 Object

Object是所有对象的父类

5.1.1 delete 指令

可以删除对象的值,使其再次读取时,返回undefined

5.1.2 静态api & 实例api

Object本身的方法(相当于java类中的静态属性),通过类名调用
Object.print
Object实例的方法(相当于java中可以被继承的实例方法),通过实例变量名 或 原型对象 调用
Object.prototype.xxx

5.1.3 静态api

getOwnPropertyNames() //用法同keys(),同时支持不可枚举的属性(该对象的描述属性、属性的描述属性)
hasOwnProperty() //自身是否持有该属性(非继承)
getOwnPropertyDescriptor(对象,属性名) //获取某个属性的描述对象,无法获取继承而来的属性

preventExtensions() 禁用对象的扩展 isExtensible()
seal() 禁用对象的配置(新增+修改属性的描述属freeze() 禁用对象的扩展、修改、删除 isFrozen()

以上三种方法可以通过在原型对象上修改属性,然后在当前对象中调用getPrototypeOf()获取原型对象读取修改 来绕过限制。
当属性是一个引用的时候,并不能限制对这个引用的属性的操作

create() 通过模板创建一个新对象实例

5.1.5 实例api

valueOf() 自动类型转换时候默认调用,默认返回对象本身
toString() toLocaleString() 字符串运算时候默认调用
默认输出一个带有构造函数名称的字符串“[Object 构造函数名称(也是对象的类型)]”
当toString()被覆写的时候,使用Object.prototype.toString.call(value),将调用原始的toString()
hasOwnProperty() 该实例对象是否具有这个属性,继承而来的属性不算
isPrototypeOf() 是否为该对象的原型对象propertyIsEnumerable() 某个属性是否可枚举

get/setPrototypeOf() 返回原型对象

5.1.6 构造方法

var obj = Object(…) //参数可以为null或undefined
var obj = new Object //构造函数,等同var obj = {}
两者语法相似,但是语义不同

5.2 属性描述对象

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为这个内部数据结构称为“属性描述对象”(attributes object)。

5.2.1 元属性

{
  value: ?,            //只要writable或configurable为true,即可被修改
  writable: boolean,    //如果get/set不为undefined,那么这里只允许为false,此时定义calue也将报错
  enumerable: boolean,    //可遍历,若false,for-in、.keys 、JOSN.stringify将无法获取这些属性
  configurable: boolean,    //可修改该属性的描述属性(除value)+删除该属性
  get: function,
  set: function
}

configurable为false,只允许writable由true变false,不允许false->true

5.2.2 存取器

get/set的函数getter/setter统称存取器(accessor)

//1.写法一:当obj.p时将调用get/set
var obj = Object.defineProperty({}, 'p', {
  get: function () {return 'getter';},
  set: function (value) {console.log('setter: ' + value);}
});
//(更常用)写法二:这种写法的configuarble和enumerable属性为true(写法一,false)
var obj = {
  get p() {return 'getter';},
  set p(value) {console.log('setter: ' + value);}
};

存取器相关的属性使用obj[属性名]拷贝只会拷贝其值,可使用Object.defineProperty来拷贝get/set方法属性

5.3 Array

5.3.1 数组的创建方法

var a = new Array(3);    //通过构造
var b = [undefined, undefined, undefined];    //通过字面量

构造出来的空数组没有 “键名”,二者的Length属性都可用

5.3.2 数组类型的判断

Array.isArray(arg)

5.3.3 数组api

js的数组像是java的Deque双向队列,不仅仅是数组,可做栈、队列
pop() push() 栈方法,将改变原来的数组
shift()、unshift() 队列方法,也会改变原数组

join() 使用一个分割符将该数组的所有元素“组成”一个字符串返回
Array.prototype.join.call() 在字符串和类数组对象上实现了一样的能力
concat() 在一个数组尾部追加一个数组,不改变原数组
reverse() 简单的不谈,改变原数组
slice() ... , 不改变
sort(func(m,n)) 默认不传的话,按字典序,改变
map(func(n)) 改变
forEach(func(ele,[index],[array])) 不改变,只操作数据
filter(func(n)) 不改变
indexOf() lastIndexOf 不存在则-1

5.4 包装对象

js原始数组类型数值、字符串、boolean,在一些条件下将自动装箱,转换成 包装类型(Number、String、Boolean)

5.4.1 Boolean

Boolean(只要不为undefined) -true

5.4.2 api

包装对象继承了Object的valueOf()、toString()

5.4.3 自动装箱的场景

'abc'.length // 3 'abc'作为一个字符串不可能有操作自己的函数

5.4.4 自动装箱的尿性(特点)

自动装箱的包装对象在结束调用之后将销毁,下一次调用将重新装箱成另一个新的包装对象->导致该对象的属性只读
如需扩展包装对象的方法,建议通过.prototype.方法名 = … 在其原型对象上操作

5.5 JSON

5.5.1 json数据格式

{ name: "张三", 'age': 32 }  // 属性名必须使用双引号
[32, 64, 128, 0xFFF] // 不能使用十六进制值
{ "name": "张三", "age": undefined } // 不能使用 undefined
{ "key": function () {return this.name;}} // 属性值不能使用函数和日期对象

5.5.2 JSON.stringify() & JSON.parse()

JSON.stringify(js对象,[数组,用于指定哪些是需要转换的属性],[格式化json字符串的参数])
当出现非法的值为undefined、函数、xml对象
如果是对象持有的值,该对象将被过滤
如果是数组持有的值,该值将替换成null
值为正则/…/的时候,该值替换成{}
自动过滤掉描述属性enumerable为false的属性
这个用于指定转换属性的数组是有说法,使用前需要查api(可以是函数)
可以在需要转换的对象中声明并实现一个toJSON()方法,一旦声明,stringify()将走toJSON()方法来转换json字符串


JSON.parse(json字符串,[用于指定需要转换的属性的数组/方法])
同上很相似

6.面对对象编程

6.1 实例对象与new命令

6.1.1 new 函数名()

通过构造函数的方式创建对象实例
构造函数建议首字母大写
构造函数的函数体中,this指定将要返回的对象实例
使用new的这个函数是一个普通函数还是一个 构造函数?
    这是一个构造函数->函数体中是否声明return?
         没有->返回this(即这个函数的实例变量)
        有->return 的这个类型是否是对象?
            是对象->返回这个对象(这里所指定的对象)
            不是对象->返回this
    这是一个普通函数(内部是没有this引用)->是否声明return ?
        没有->返回{}
        有->返回的类型是一个对象吗?
            是对象->返回这个对象
            不是一个对象->返回{}

new命令的简化流程的代码实现

function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
  // 将 arguments 对象转为数组
  var args = [].slice.call(arguments);
  // 取出构造函数
  var constructor = args.shift();
  // 创建一个空对象,继承构造函数的 prototype 属性
  var context = Object.create(constructor.prototype);
  // 执行构造函数
  var result = constructor.apply(context, args);
  // 如果返回结果是对象,就直接返回,否则返回 context 对象
  return (typeof result === 'object' && result != null) ? result : context;
}
// 实例var actor = _new(Person, '张三', 28);

6.1.2 Object.create(现有对象)

与new的区别:create()不通过构造函数,而是将一个现有的对象作为模板来返回一个新实例对象
返回的这个新实例对象将自动继承前者的属性与方法

6.1.3 函数体中new.target命令

该命令所在的函数是否是通过new命令调用的?
是->new.target指向当前函数
不是->值为undefined

6.2 this

指向运行时所在的环境对象(也可以说是 上一层级,比若说,全局的环境对象即window)

为什么设计this关键字?
js中对象的内地地址 指向了 该对象的属性(可以是方法)的内存地址
不同对象如何存在相同的属性,将引用同一块内存地址,这个该属性的值(假如说是返回一段计算结果)将取决与它运行时的环境(该环境中的同名变量的值)

多层嵌套下,谨慎使用

常见的应用场景
全局…
构造函数->将要返回的实例对象
对象的一个方法属性->该对象

6.2.1 放一个栗子

var a = {
  b: {
    m: function() {console.log(this.p);},
    p: 'Hello'
  }
  v:'hello',
  q:['a1','a2'],  
  f1: function f1() {this.q.forEach(function (item) {console.log(this.v + ' ' + item);});} ,
  f2: function f2() {var that = this; this.q.forEach(function (item) {console.log(that.v + ' ' + item);});}  ,
  f3: function f3() {this.q.forEach(function (item) {console.log(this.v + ' ' + item);} ,this);} ,
};

//例1
var hello = a.b.m; hello();     //undefined ,this指向m变量所在的环境对象(m变量的上一层级对象),m的函数指向是不是很像是指向了全局的一个匿名函数?
//例2
var hello = a.b; hello.m();     //Hello ,this指向a.b ,这里我们通过改变函数的主调来使得this如我们期望的指向
//例3
a.f1();    // undefined a1 \n undefined a2 ,this指向全局,道理同例1
//例4
a.f2();    // hello a1 \n hello a2 ,通过that保存该函数所在环境对象
//例5
a.f3();    //输出结果同例4 ,通过将函数的this传入forEach()中作为参数

6.2.2 回调中的this

var o = new Object();
o.f = function () {console.log(this === o);}
// jQuery 的写法
$('#button').on('click', o.f);

像这里的this指向的就是按钮标签的dom对象,不再是主调o对象

6.2.3 显示确定this

指定函数执行时的作用域


Object.prototype.方法名.call(作用域对象 ,… 函数传参) & Object.方法名.call(同前者)

如何入参的作用域对象为()、null、undefined,则默认传入全局对象
如果入参的作用域对象为原始数据类型,那么该值将自动装箱成包装对象


Object.prototype.方法名.apply(作用域对象,函数传参的数组) & Object.方法名.apply(同前者)

如果入参的数组中包含形如([‘2’, ,1])这种空元素的话,空元素将被替换成undefined

一个绑定回调的栗子

var o = new Object();
o.f = function () {console.log(this === o);}

var f = function (){
  o.f.apply(o);
  // 或者 o.f.call(o);
};

// jQuery 的写法
$('#button').on('click', f);

Object.prototype.方法名.bind(作用域对象 ,…函数传参) & Object.方法名.bind(同前者)

与call()、apply() 区别是:可以先绑定后执行,并且不需要在绑定作用域的同时声明函数实现
函数传参可以不传所有参数,可以传入一部分,另一部分在调用的时候传入
该方法每次将返回一个新的函数,如果需要写成匿名的绑定(不做返回值的保存),事件监听中请考虑如何解除绑定
在回调函数中,被调函数中的this将指向环境对象(回调函数对象),因此可以在使用被调函数(被调函数.bind(作用域))
bind()与call()/apply()结合使用,可以起到改变函数的调用方式的作用(比如:直接在原型对象上调用实例对象的方法,具体参考文档)

6.3 对象的继承

ES5基于prototype 原型对象实现
所有对象都有自己的原型对象(除null,Object的原型对象为null)

6.3.1 api

    valueOf()
    toString()
    construvtor 默认指向Prototype所在对象的构造函数
        该对象有一个name属性,返回构造函数的名称
        修改了原型对象,建议同时修改constructor属性,防止引用出错

6.3.2 原型链

原型对象也是对象,它也有自己的原型对象,因此就形成了一条"原型链"

6.3.3 对象 instanceof 构造函数

表示左边对象是否是右边对象的实例
效果同 构造函数.prototype.isPrototypeOf(对象)
只要对象不为null,就不会失真,原始类型也会失真
需要检查整个原型链
同一个实例对象,可能会对多个构造函数返回true

6.3.4 构造函数的继承

步骤:
1.调用Super父类构造函数
2.让子类的原型指向父类的原型

6.3.5 多重继承

js不支持,但是可以通过Object.assign()绕过限制(Object.assign()使用见文档,主要用于合并js对象)

6.3.6 获取原型对象的方式

obj._ptoto_ //只有浏览器环境下才会部署
obj.constructor.prototype //手动修改原型对象时,可能失效
Object.getPrototypeOf(obj) //建议使用

6.3.7 obj.constructor.prototype 失效的解决方案

同时修改Object.prototype.constructor

//预先声明函数对象设为C的原型对象
var P = function () {};
var p = new P();

var C = function () {};
C.prototype = p;    //使C原型对象继承自P对象
C.prototype.constructor = C    //将原型对象的构造参数也同时改成C    

//执行测试
var c = new C();
c.constructor.prototype === p    //true

7.DOM

8.事件

8.1 event对象

浏览器中,事件触发后,封装了该事件相关信息并传递给事件监听函数的一个对象。

8.2 事件传播

冒泡 dom -> event -> 外层dom…
捕获 dom <- event <- 外层dom…
顺序:捕获->冒泡。好像IE不太一样…

8.2.1 传播状态

0,事件目前没有发生。
1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。
2,事件到达目标节点,即Event.target属性指向的那个节点。
3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。

8.3 元素的的事件监听

8.3.1 this

指向当前监听函数所在的dom元素

8.4 api

bubbles 是否冒泡(只读)
eventPhase  目前所处的传播状态(只读)
cancelable 可否取消(只读) 
cancelBubble 可否冒泡,用于阻止事件传播
defaultPrevented 是否调用过preventDefault(),(只读)
currentTarget 当前传播的节点
target 原始事件触发所在的节点
isTrusted  是否人为产生(非脚本触发)
timeStamp
type

preventDefault() 取消当前节点的事件监听函数,不影响传播(节点)
stopPropagation() 阻止事件传播,不包括当前节点
stopImmediatePropagation() 阻止事件传播,包括当前节点
composedPath() 返回一个数组,表示冒泡的节点路径

8.5 一个有趣的现象:浏览器输出event对象的日志中currentTarget=null?

currentTarget是当前的对象,使用console.info输出日志的时候,此时dom的事件传播已经结束了,故null

如果你换做——直接输出console.info(event.currentTarget)则可以成功输出当前的dom对象(event.currentTarget.value可以获取其值)

9.浏览器模型

10.附录:网页元素接口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ThinkPHP 是一个优秀的PHP框架,可以帮助我们快速开发高质量的 Web 应用程序。因此,使用 ThinkPHP 来开发 OA 系统是非常方便和实用的。下面我们来讨论一下如何使用 ThinkPHP 来开发一个人力资源管理系统。 1. 创建数据库 首先,我们需要创建一个数据库来存储我们的数据。在该数据库中,我们需要创建一些表来存储员工信息、部门信息、职位信息、薪资信息等等。我们可以使用 MySQL 数据库来创建这些表。 2. 创建 ThinkPHP 项目 在创建完数据库之后,我们需要创建一个 ThinkPHP 项目来实现我们的 OA 系统。我们可以使用 ThinkPHP 官方提供的命令行工具来创建项目,具体命令如下: ``` composer create-project topthink/think oa ``` 执行完上述命令后,会在当前目录下创建一个名为 oa 的 ThinkPHP 项目。 3. 配置数据库 在项目创建好之后,我们需要配置数据库连接信息。在项目的 `.env` 文件中,我们可以配置数据库的相关信息,如下所示: ``` # 数据库类型 DB_CONNECTION=mysql # 数据库地址 DB_HOST=127.0.0.1 # 数据库端口 DB_PORT=3306 # 数据库名 DB_DATABASE=oa # 数据库用户名 DB_USERNAME=root # 数据库密码 DB_PASSWORD= ``` 我们需要根据自己的实际情况来修改这些配置项。 4. 创建控制器和模型 在 ThinkPHP 中,我们可以通过创建控制器和模型来实现 OA 系统的功能。我们可以使用命令行工具来快速创建控制器和模型,具体命令如下: ``` # 创建控制器 php think make:controller Index # 创建模型 php think make:model User ``` 执行完上述命令后,会在项目中生成一个名为 Index 的控制器和一个名为 User 的模型。 5. 实现功能 在创建好控制器和模型之后,我们就可以开始实现 OA 系统的功能了。比如,我们可以实现员工信息的添加、修改、删除、查询等操作,部门信息的添加、修改、删除、查询等操作,职位信息的添加、修改、删除、查询等操作,薪资信息的添加、修改、删除、查询等操作等等。我们可以在控制器中编写处理逻辑,在模型中编写数据库操作。最后,我们可以使用视图来展示数据。 6. 测试系统 在实现完功能之后,我们需要对系统进行测试。我们可以使用浏览器或者 Postman 等工具来测试系统的功能是否正常。如果有问题,我们可以根据错误提示来进行调试和修复。 总之,使用 ThinkPHP 来开发 OA 系统是非常方便和实用的。我们可以根据自己的实际情况来定制系统的功能和界面,从而更好地管理公司的人力资源。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肯尼思布赖恩埃德蒙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值