半葬歌

文章 评论 浏览 31

半葬歌 2022-05-04 13:57:21

csrf例子:假如一家银行用以运行转账操作的URL地址如下: http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
那么,一个恶意攻击者可以在另一个网站上放置如下代码: <img src="<http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman>">
如果有账户名为Alice的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。

这个 SameSite=strict 就可以对付了。

第 28 题:cookie 和 token 都存放在 header 中,为什么不会劫持 token?

半葬歌 2022-05-04 13:56:53
var obj = {
  '2': 3,
  '3': 4,
  length: 2,
  splice: Array.prototype.splice,
  push: Array.prototype.push
}
obj.push(1)
obj.push(2)
console.log(obj)

数组对象

JavaScript 数组的有一些特性是其他对象所没有的:

  • 当有新的元素添加到列表中的时候,自动更新 length 属性

  • 设置 length 为一个较小值的时候将会截断数组

  • Array.prototype 中继承一些有用的方法

  • 其类属性为 Array

这些特性让 JavaScript 数组和常规对象有明显的区别。但是它们不是定义数组的本质特性。

类数组对象

把拥有一个数值 length 属性和对应非负整数属性的对象看作一种类型的数组

我们定义的 function 函数中 Arguments 对象就是一个类数组对象,同样在客户端 JavaScript 中,一些 DOM 的方法比如 document.getElementsByTagName() 也是返回的类数组对象

// 判断是否是类数组对象
const isArrayLike = obj =>
  obj && // 非 null undefined
  typeof obj === 'object' && // 是对象
  isFinite(obj.length) && // 是有穷数
  obj.length >= 0 && // 为非负数
  obj.length === Math.floor(obj.length) && // 整数
  obj.length < Math.pow(2, 32) // < 4294967296

同时 JavaScript 数组方法是特意定义为通用的,因此它们不仅应用在真正的数组而且在类数组对象上都能正确的工作。类数组对象没有继承自 Array.prototype,不能直接直接调用数组的方法,但是也是可以通过 Function.call 方法调用。

那么我们改变一下题目

var obj2 = {
  '2': 3,
  '3': 4,
  length: 2
}

Array.prototype.push.call(obj2, 1)
Array.prototype.push.call(obj2, 2)
// 这样的到的有效结果是一样的  {2: 1, 3: 2, length: 4}

// 通过数组的方法 // 1 2
Array.prototype.forEach.call(obj2, item => console.log(item))
// 其本质还是一个类数组

结果

这是题目在 Chrome 浏览器控制台输出结果

Chrome控制台输出题目结果

我们改变的题目输出结果

改变的题目控制台输出题目结果

我们可以看到有效结果是一样的,那么为什么结果会是如此呢?

push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。 push 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。MDN

so ~

// obj.push(1)
// 等同于
obj['2'] = 1
obj.length++ // length = 3
// obj.push(2)
// 等同于
obj['3'] = 2
obj.length++ // length = 4
// 那么之后的结果就是我们看到的了

但是还是有不一样的地方比如

[empty × 2, 1, 2, splice: ƒ, push: ƒ]{2: 1, 3: 2, length: 4}

那么我们接下来继续看

继续看比较

只有当对象 splice 属性是一个 Function 的时候输出才为 [empty × 2, 1, 2, splice: ƒ, push: ƒ]

那么为此我又去 Firefox 控制台下面试了一下,结果如下图:

火狐控制台尝试

跟 Chrome 没有定义 splice 为 Function 是一致的

所以说可能是 Chrome 对其做的优化吧。

第 46 题:输出以下代码执行的结果并解释为什么?

半葬歌 2022-05-04 13:48:38

关于 {} + {} 浏览器不同的输出,我刚好研究了下
原文在这
大概在chrome版本49之前,Chrome控制台上面的输出结果基本和Firefox一致,之后在chrome上有人提出bug,Issue 499864,大概意思就是说我在控制台输入{a: 4, b: 5}你给我报个错干嘛,我就是想要一个对象而已。Chrome没过多久就修复了,修复的方式也特别666,就是凡是语句以{开头,以}结尾,我解析的时候就包裹一层括号在外面。git记录,里面的关键代码如下:

+    if (/^s*{/.test(text) && /}s*$/.test(text))
+        text = '(' + text + ')';

故在chrome下{} + {} 即是({} + {})
故这里的花括号正确地被解析成了对象

JavaScript 深入之类型转换(下)

半葬歌 2022-05-04 12:40:03

API 探索

Redux API

1. createStore(reducer, [preloadedState], enhancer)

创建一个 Redux store 来以存放应用中所有的 state。详情可见 Redux API,这里主要强调两点:

  1. preloadedState:初始时的 state。在同构中会用到,比如从一个session恢复数据。

当 store 创建后,Redux 会 dispatch action({ type: ActionTypes.INIT })) 到 reducer 上,得到初始的 state 来填充 store。所以你的初始 state 是 preloadedState 在 reducers 处理 ActionTypes.INIT action 后的结果。 https://github.com/reactjs/redux/blob/v3.6.0/src/createStore.js

  1. enhancer:如果有 enhancer,那么会首先得到增强的 createStore,然后再createStore(reducer, [preloadedState])

https://github.com/reactjs/redux/blob/v3.6.0/src/createStore.js 可结合下面 applyMiddleware一起看。

2. middleware 与 applyMiddleware(...middlewares)

我们可以用 middleware 来扩展 Redux。Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。

middleware 的函数签名是 ({ getState, dispatch }) => next => action

如下是两个 middleware:

// logger middleware
function logger({ getState }) {
  return (next) => (action) => {
    console.log('will dispatch', action)

    // 调用 middleware 链中下一个 middleware 的 dispatch。
    let returnValue = next(action)

    console.log('state after dispatch', getState())

    // 一般会是 action 本身,除非
    // 后面的 middleware 修改了它。
    return returnValue
  }
}

// thunk middleware
function thunk({ dispatch, getState }) {
  return (next) => (action) => {
    if (typeof action === 'function') {
      return action(dispatch, getState);
    }

    return next(action);
  }
}

applyMiddleware 返回一个应用了 middleware 后的 store enhancer。这个 store enhancer 的签名是 createStore => createStore,但是最简单的使用方法就是直接作为最后一个 enhancer 参数传递给 createStore() 函数。

再来看下 applyMiddleware

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch
    var chain = []

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    // chain 是 [(next) => (action) => action, ...]
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    // compose(...chain) 返回这样一个函数:
    // 对 chain 进行 reduce,从右向左执行,每次的结果作为下次执行的输入
    dispatch = compose(...chain)(store.dispatch)
    // 最终的 dispatch 是这样的:(action) => action

    return {
      ...store,
      dispatch
    }
  }
}

可以看到(假设 enhancerTest = applyMiddleware(A, B, C)):

  1. middleware 其实只是劫持/包装了 dispatch
  2. dispatch 本质上是同步的,但我们可以通过 thunk 等延迟执行 dispatch
  3. chain[index](dispatch) --> (action) => action,即我们得到的 dispatch 是一个层层嵌套的 (action) => action 函数。
  4. 除了最右侧的 C 得到的 next 是原本的 dispatch,剩下的都是被层层嵌套的 (action) => action 函数,并且越右侧越嵌套在里面,所以当 dispatch(action) 调用时,将会以下面顺序执行:A -> B -> C -> B -> AC 之前 A/B 都只执行了 next 之前的逻辑,之后各自完全执行。

3. combineReducers(reducers)

把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数。

真的很简单,从逻辑上来讲,就是:

combineReducers({
  keyA: reducerA,
  keyB: reducerB
})

// --->

function recuderAll (prevState, action) {
  return {
    keyA: reducerA(prevState.keyA, action),
    keyB: reducerB(prevState.keyB, action)
  }
}

核心就是干了上面的事,只是多了一些判断和检查。

4. bindActionCreators(actionCreators, dispatch)

把 action creators 转成拥有同名 keys 的对象,但使用 dispatch 把每个 action creator 包围起来,这样可以直接调用它们。

const actionCreators = {
  updateOrAddFilter(filter) {
    type: UPDATE_OR_ADD_FILTER,
    filter
  },
  removeFilter(type) {
    type: REMOVE_FILTER,
    filterType: type
  }
}

bindActionCreators(actionCreators, dispatch)

// -->

{
  updateOrAddFilter: (...args) => dispatch(original_updateOrAddFilter(...args)),
  removeFilter: (...args) => dispatch(original_removeFilter(...args)),
}

核心就是自动 dispatch ,这样我们可以在 react 组件里直接调用,Redux store 就能收到 action。


React Redux API

1. Provider

用法:

ReactDOM.render(
  <Provider store={store}>
    <MyRootComponent />
  </Provider>,
  rootEl
)

源码:

// storeKey 默认是 'store'
class Provider extends Component {
    getChildContext() {
      return { [storeKey]: this[storeKey], [subscriptionKey]: null }
    }

    constructor(props, context) {
      super(props, context)
      this[storeKey] = props.store;
    }

    render() {
      return Children.only(this.props.children)
    }
}
Provider.propTypes = {
    store: storeShape.isRequired,
    children: PropTypes.element.isRequired,
}
Provider.childContextTypes = {
    [storeKey]: storeShape.isRequired,
    [subscriptionKey]: subscriptionShape,
}

注意到, Provider 应用了 React Context,子组件都可以去访问 store

2. connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

connect 的函数签名是 ([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) => (WrappedComponent) => ConnectComponent,最后返回的 onnectComponent 可以通过 context 去访问 store

connect API 比较复杂,这里主要讲下前两个参数。

  • [mapStateToProps(state, [ownProps]): stateProps] (Function): 如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,你的组件将不会监听 Redux store。
  • [mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,而且这个对象会与 Redux store 绑定在一起,其中所定义的方法名将作为属性名,合并到组件的 props 中。

Redux 入门摘要

更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文